SyncManager.java revision cd75706117432e33d11639e675bcff50479a6bb9
1/* 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 17package android.content; 18 19import android.accounts.Account; 20import android.accounts.AccountAndUser; 21import android.accounts.AccountManager; 22import android.accounts.AccountManagerService; 23import android.app.ActivityManager; 24import android.app.AlarmManager; 25import android.app.Notification; 26import android.app.NotificationManager; 27import android.app.PendingIntent; 28import android.content.SyncStorageEngine.OnSyncRequestListener; 29import android.content.pm.ApplicationInfo; 30import android.content.pm.PackageManager; 31import android.content.pm.ProviderInfo; 32import android.content.pm.RegisteredServicesCache; 33import android.content.pm.RegisteredServicesCacheListener; 34import android.content.pm.ResolveInfo; 35import android.content.pm.UserInfo; 36import android.net.ConnectivityManager; 37import android.net.NetworkInfo; 38import android.os.Bundle; 39import android.os.Handler; 40import android.os.HandlerThread; 41import android.os.IBinder; 42import android.os.Looper; 43import android.os.Message; 44import android.os.PowerManager; 45import android.os.Process; 46import android.os.RemoteException; 47import android.os.SystemClock; 48import android.os.SystemProperties; 49import android.os.UserHandle; 50import android.os.UserManager; 51import android.os.WorkSource; 52import android.provider.Settings; 53import android.text.format.DateUtils; 54import android.text.format.Time; 55import android.util.EventLog; 56import android.util.Log; 57import android.util.Pair; 58import android.util.Slog; 59 60import com.android.internal.R; 61import com.android.internal.util.IndentingPrintWriter; 62import com.google.android.collect.Lists; 63import com.google.android.collect.Maps; 64import com.google.android.collect.Sets; 65 66import java.io.FileDescriptor; 67import java.io.PrintWriter; 68import java.util.ArrayList; 69import java.util.Arrays; 70import java.util.Collection; 71import java.util.Collections; 72import java.util.Comparator; 73import java.util.HashMap; 74import java.util.HashSet; 75import java.util.Iterator; 76import java.util.List; 77import java.util.Map; 78import java.util.Random; 79import java.util.Set; 80import java.util.concurrent.CountDownLatch; 81 82/** 83 * @hide 84 */ 85public class SyncManager { 86 private static final String TAG = "SyncManager"; 87 88 /** Delay a sync due to local changes this long. In milliseconds */ 89 private static final long LOCAL_SYNC_DELAY; 90 91 /** 92 * If a sync takes longer than this and the sync queue is not empty then we will 93 * cancel it and add it back to the end of the sync queue. In milliseconds. 94 */ 95 private static final long MAX_TIME_PER_SYNC; 96 97 static { 98 final boolean isLargeRAM = ActivityManager.isLargeRAM(); 99 int defaultMaxInitSyncs = isLargeRAM ? 5 : 2; 100 int defaultMaxRegularSyncs = isLargeRAM ? 2 : 1; 101 MAX_SIMULTANEOUS_INITIALIZATION_SYNCS = 102 SystemProperties.getInt("sync.max_init_syncs", defaultMaxInitSyncs); 103 MAX_SIMULTANEOUS_REGULAR_SYNCS = 104 SystemProperties.getInt("sync.max_regular_syncs", defaultMaxRegularSyncs); 105 LOCAL_SYNC_DELAY = 106 SystemProperties.getLong("sync.local_sync_delay", 30 * 1000 /* 30 seconds */); 107 MAX_TIME_PER_SYNC = 108 SystemProperties.getLong("sync.max_time_per_sync", 5 * 60 * 1000 /* 5 minutes */); 109 SYNC_NOTIFICATION_DELAY = 110 SystemProperties.getLong("sync.notification_delay", 30 * 1000 /* 30 seconds */); 111 } 112 113 private static final long SYNC_NOTIFICATION_DELAY; 114 115 /** 116 * When retrying a sync for the first time use this delay. After that 117 * the retry time will double until it reached MAX_SYNC_RETRY_TIME. 118 * In milliseconds. 119 */ 120 private static final long INITIAL_SYNC_RETRY_TIME_IN_MS = 30 * 1000; // 30 seconds 121 122 /** 123 * Default the max sync retry time to this value. 124 */ 125 private static final long DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS = 60 * 60; // one hour 126 127 /** 128 * How long to wait before retrying a sync that failed due to one already being in progress. 129 */ 130 private static final int DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS = 10; 131 132 private static final int INITIALIZATION_UNBIND_DELAY_MS = 5000; 133 134 private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*"; 135 private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm"; 136 private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock"; 137 138 private static final int MAX_SIMULTANEOUS_REGULAR_SYNCS; 139 private static final int MAX_SIMULTANEOUS_INITIALIZATION_SYNCS; 140 141 private Context mContext; 142 143 private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0]; 144 145 // TODO: add better locking around mRunningAccounts 146 private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY; 147 148 volatile private PowerManager.WakeLock mHandleAlarmWakeLock; 149 volatile private PowerManager.WakeLock mSyncManagerWakeLock; 150 volatile private boolean mDataConnectionIsConnected = false; 151 volatile private boolean mStorageIsLow = false; 152 153 private final NotificationManager mNotificationMgr; 154 private AlarmManager mAlarmService = null; 155 156 private SyncStorageEngine mSyncStorageEngine; 157 158 // @GuardedBy("mSyncQueue") 159 private final SyncQueue mSyncQueue; 160 161 protected final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList(); 162 163 // set if the sync active indicator should be reported 164 private boolean mNeedSyncActiveNotification = false; 165 166 private final PendingIntent mSyncAlarmIntent; 167 // Synchronized on "this". Instead of using this directly one should instead call 168 // its accessor, getConnManager(). 169 private ConnectivityManager mConnManagerDoNotUseDirectly; 170 171 protected SyncAdaptersCache mSyncAdapters; 172 173 private BroadcastReceiver mStorageIntentReceiver = 174 new BroadcastReceiver() { 175 public void onReceive(Context context, Intent intent) { 176 String action = intent.getAction(); 177 if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) { 178 if (Log.isLoggable(TAG, Log.VERBOSE)) { 179 Log.v(TAG, "Internal storage is low."); 180 } 181 mStorageIsLow = true; 182 cancelActiveSync(null /* any account */, UserHandle.USER_ALL, 183 null /* any authority */); 184 } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { 185 if (Log.isLoggable(TAG, Log.VERBOSE)) { 186 Log.v(TAG, "Internal storage is ok."); 187 } 188 mStorageIsLow = false; 189 sendCheckAlarmsMessage(); 190 } 191 } 192 }; 193 194 private BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { 195 public void onReceive(Context context, Intent intent) { 196 mSyncHandler.onBootCompleted(); 197 } 198 }; 199 200 private BroadcastReceiver mBackgroundDataSettingChanged = new BroadcastReceiver() { 201 public void onReceive(Context context, Intent intent) { 202 if (getConnectivityManager().getBackgroundDataSetting()) { 203 scheduleSync(null /* account */, UserHandle.USER_ALL, null /* authority */, 204 new Bundle(), 0 /* delay */, 205 false /* onlyThoseWithUnknownSyncableState */); 206 } 207 } 208 }; 209 210 private BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() { 211 public void onReceive(Context context, Intent intent) { 212 updateRunningAccounts(); 213 214 // Kick off sync for everyone, since this was a radical account change 215 scheduleSync(null, UserHandle.USER_ALL, null, null, 0 /* no delay */, false); 216 } 217 }; 218 219 private final PowerManager mPowerManager; 220 221 // Use this as a random offset to seed all periodic syncs 222 private int mSyncRandomOffsetMillis; 223 224 private final UserManager mUserManager; 225 226 private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds 227 private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours 228 229 private List<UserInfo> getAllUsers() { 230 return mUserManager.getUsers(); 231 } 232 233 private boolean containsAccountAndUser(AccountAndUser[] accounts, Account account, int userId) { 234 boolean found = false; 235 for (int i = 0; i < accounts.length; i++) { 236 if (accounts[i].userId == userId 237 && accounts[i].account.equals(account)) { 238 found = true; 239 break; 240 } 241 } 242 return found; 243 } 244 245 public void updateRunningAccounts() { 246 mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts(); 247 248 if (mBootCompleted) { 249 doDatabaseCleanup(); 250 } 251 252 for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) { 253 if (!containsAccountAndUser(mRunningAccounts, 254 currentSyncContext.mSyncOperation.account, 255 currentSyncContext.mSyncOperation.userId)) { 256 Log.d(TAG, "canceling sync since the account is no longer running"); 257 sendSyncFinishedOrCanceledMessage(currentSyncContext, 258 null /* no result since this is a cancel */); 259 } 260 } 261 262 // we must do this since we don't bother scheduling alarms when 263 // the accounts are not set yet 264 sendCheckAlarmsMessage(); 265 } 266 267 private void doDatabaseCleanup() { 268 for (UserInfo user : mUserManager.getUsers(true)) { 269 // Skip any partially created/removed users 270 if (user.partial) continue; 271 Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(user.id); 272 mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id); 273 } 274 } 275 276 private BroadcastReceiver mConnectivityIntentReceiver = 277 new BroadcastReceiver() { 278 public void onReceive(Context context, Intent intent) { 279 final boolean wasConnected = mDataConnectionIsConnected; 280 281 // don't use the intent to figure out if network is connected, just check 282 // ConnectivityManager directly. 283 mDataConnectionIsConnected = readDataConnectionState(); 284 if (mDataConnectionIsConnected) { 285 if (!wasConnected) { 286 if (Log.isLoggable(TAG, Log.VERBOSE)) { 287 Log.v(TAG, "Reconnection detected: clearing all backoffs"); 288 } 289 mSyncStorageEngine.clearAllBackoffs(mSyncQueue); 290 } 291 sendCheckAlarmsMessage(); 292 } 293 } 294 }; 295 296 private boolean readDataConnectionState() { 297 NetworkInfo networkInfo = getConnectivityManager().getActiveNetworkInfo(); 298 return (networkInfo != null) && networkInfo.isConnected(); 299 } 300 301 private BroadcastReceiver mShutdownIntentReceiver = 302 new BroadcastReceiver() { 303 public void onReceive(Context context, Intent intent) { 304 Log.w(TAG, "Writing sync state before shutdown..."); 305 getSyncStorageEngine().writeAllState(); 306 } 307 }; 308 309 private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() { 310 @Override 311 public void onReceive(Context context, Intent intent) { 312 String action = intent.getAction(); 313 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 314 if (userId == UserHandle.USER_NULL) return; 315 316 if (Intent.ACTION_USER_REMOVED.equals(action)) { 317 onUserRemoved(userId); 318 } else if (Intent.ACTION_USER_STARTING.equals(action)) { 319 onUserStarting(userId); 320 } else if (Intent.ACTION_USER_STOPPING.equals(action)) { 321 onUserStopping(userId); 322 } 323 } 324 }; 325 326 private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM"; 327 private final SyncHandler mSyncHandler; 328 329 private volatile boolean mBootCompleted = false; 330 331 private ConnectivityManager getConnectivityManager() { 332 synchronized (this) { 333 if (mConnManagerDoNotUseDirectly == null) { 334 mConnManagerDoNotUseDirectly = (ConnectivityManager)mContext.getSystemService( 335 Context.CONNECTIVITY_SERVICE); 336 } 337 return mConnManagerDoNotUseDirectly; 338 } 339 } 340 341 /** 342 * Should only be created after {@link ContentService#systemReady()} so that 343 * {@link PackageManager} is ready to query. 344 */ 345 public SyncManager(Context context, boolean factoryTest) { 346 // Initialize the SyncStorageEngine first, before registering observers 347 // and creating threads and so on; it may fail if the disk is full. 348 mContext = context; 349 350 SyncStorageEngine.init(context); 351 mSyncStorageEngine = SyncStorageEngine.getSingleton(); 352 mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() { 353 public void onSyncRequest(Account account, int userId, String authority, 354 Bundle extras) { 355 scheduleSync(account, userId, authority, extras, 0, false); 356 } 357 }); 358 359 mSyncAdapters = new SyncAdaptersCache(mContext); 360 mSyncQueue = new SyncQueue(mSyncStorageEngine, mSyncAdapters); 361 362 HandlerThread syncThread = new HandlerThread("SyncHandlerThread", 363 Process.THREAD_PRIORITY_BACKGROUND); 364 syncThread.start(); 365 mSyncHandler = new SyncHandler(syncThread.getLooper()); 366 367 mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() { 368 @Override 369 public void onServiceChanged(SyncAdapterType type, int userId, boolean removed) { 370 if (!removed) { 371 scheduleSync(null, UserHandle.USER_ALL, type.authority, null, 0 /* no delay */, 372 false /* onlyThoseWithUnkownSyncableState */); 373 } 374 } 375 }, mSyncHandler); 376 377 mSyncAlarmIntent = PendingIntent.getBroadcast( 378 mContext, 0 /* ignored */, new Intent(ACTION_SYNC_ALARM), 0); 379 380 IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); 381 context.registerReceiver(mConnectivityIntentReceiver, intentFilter); 382 383 if (!factoryTest) { 384 intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); 385 context.registerReceiver(mBootCompletedReceiver, intentFilter); 386 } 387 388 intentFilter = new IntentFilter(ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED); 389 context.registerReceiver(mBackgroundDataSettingChanged, intentFilter); 390 391 intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW); 392 intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); 393 context.registerReceiver(mStorageIntentReceiver, intentFilter); 394 395 intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN); 396 intentFilter.setPriority(100); 397 context.registerReceiver(mShutdownIntentReceiver, intentFilter); 398 399 intentFilter = new IntentFilter(); 400 intentFilter.addAction(Intent.ACTION_USER_REMOVED); 401 intentFilter.addAction(Intent.ACTION_USER_STARTING); 402 intentFilter.addAction(Intent.ACTION_USER_STOPPING); 403 mContext.registerReceiverAsUser( 404 mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null); 405 406 if (!factoryTest) { 407 mNotificationMgr = (NotificationManager) 408 context.getSystemService(Context.NOTIFICATION_SERVICE); 409 context.registerReceiver(new SyncAlarmIntentReceiver(), 410 new IntentFilter(ACTION_SYNC_ALARM)); 411 } else { 412 mNotificationMgr = null; 413 } 414 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 415 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 416 417 // This WakeLock is used to ensure that we stay awake between the time that we receive 418 // a sync alarm notification and when we finish processing it. We need to do this 419 // because we don't do the work in the alarm handler, rather we do it in a message 420 // handler. 421 mHandleAlarmWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 422 HANDLE_SYNC_ALARM_WAKE_LOCK); 423 mHandleAlarmWakeLock.setReferenceCounted(false); 424 425 // This WakeLock is used to ensure that we stay awake while running the sync loop 426 // message handler. Normally we will hold a sync adapter wake lock while it is being 427 // synced but during the execution of the sync loop it might finish a sync for 428 // one sync adapter before starting the sync for the other sync adapter and we 429 // don't want the device to go to sleep during that window. 430 mSyncManagerWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 431 SYNC_LOOP_WAKE_LOCK); 432 mSyncManagerWakeLock.setReferenceCounted(false); 433 434 mSyncStorageEngine.addStatusChangeListener( 435 ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, new ISyncStatusObserver.Stub() { 436 public void onStatusChanged(int which) { 437 // force the sync loop to run if the settings change 438 sendCheckAlarmsMessage(); 439 } 440 }); 441 442 if (!factoryTest) { 443 // Register for account list updates for all users 444 mContext.registerReceiverAsUser(mAccountsUpdatedReceiver, 445 UserHandle.ALL, 446 new IntentFilter(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION), 447 null, null); 448 } 449 450 // Pick a random second in a day to seed all periodic syncs 451 mSyncRandomOffsetMillis = mSyncStorageEngine.getSyncRandomOffset() * 1000; 452 } 453 454 /** 455 * Return a random value v that satisfies minValue <= v < maxValue. The difference between 456 * maxValue and minValue must be less than Integer.MAX_VALUE. 457 */ 458 private long jitterize(long minValue, long maxValue) { 459 Random random = new Random(SystemClock.elapsedRealtime()); 460 long spread = maxValue - minValue; 461 if (spread > Integer.MAX_VALUE) { 462 throw new IllegalArgumentException("the difference between the maxValue and the " 463 + "minValue must be less than " + Integer.MAX_VALUE); 464 } 465 return minValue + random.nextInt((int)spread); 466 } 467 468 public SyncStorageEngine getSyncStorageEngine() { 469 return mSyncStorageEngine; 470 } 471 472 private void ensureAlarmService() { 473 if (mAlarmService == null) { 474 mAlarmService = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); 475 } 476 } 477 478 /** 479 * Initiate a sync. This can start a sync for all providers 480 * (pass null to url, set onlyTicklable to false), only those 481 * providers that are marked as ticklable (pass null to url, 482 * set onlyTicklable to true), or a specific provider (set url 483 * to the content url of the provider). 484 * 485 * <p>If the ContentResolver.SYNC_EXTRAS_UPLOAD boolean in extras is 486 * true then initiate a sync that just checks for local changes to send 487 * to the server, otherwise initiate a sync that first gets any 488 * changes from the server before sending local changes back to 489 * the server. 490 * 491 * <p>If a specific provider is being synced (the url is non-null) 492 * then the extras can contain SyncAdapter-specific information 493 * to control what gets synced (e.g. which specific feed to sync). 494 * 495 * <p>You'll start getting callbacks after this. 496 * 497 * @param requestedAccount the account to sync, may be null to signify all accounts 498 * @param userId the id of the user whose accounts are to be synced. If userId is USER_ALL, 499 * then all users' accounts are considered. 500 * @param requestedAuthority the authority to sync, may be null to indicate all authorities 501 * @param extras a Map of SyncAdapter-specific information to control 502 * syncs of a specific provider. Can be null. Is ignored 503 * if the url is null. 504 * @param delay how many milliseconds in the future to wait before performing this 505 * @param onlyThoseWithUnkownSyncableState 506 */ 507 public void scheduleSync(Account requestedAccount, int userId, String requestedAuthority, 508 Bundle extras, long delay, boolean onlyThoseWithUnkownSyncableState) { 509 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 510 511 final boolean backgroundDataUsageAllowed = !mBootCompleted || 512 getConnectivityManager().getBackgroundDataSetting(); 513 514 if (extras == null) extras = new Bundle(); 515 516 Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false); 517 if (expedited) { 518 delay = -1; // this means schedule at the front of the queue 519 } 520 521 AccountAndUser[] accounts; 522 if (requestedAccount != null && userId != UserHandle.USER_ALL) { 523 accounts = new AccountAndUser[] { new AccountAndUser(requestedAccount, userId) }; 524 } else { 525 // if the accounts aren't configured yet then we can't support an account-less 526 // sync request 527 accounts = mRunningAccounts; 528 if (accounts.length == 0) { 529 if (isLoggable) { 530 Log.v(TAG, "scheduleSync: no accounts configured, dropping"); 531 } 532 return; 533 } 534 } 535 536 final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false); 537 final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false); 538 if (manualSync) { 539 extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); 540 extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); 541 } 542 final boolean ignoreSettings = 543 extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false); 544 545 int source; 546 if (uploadOnly) { 547 source = SyncStorageEngine.SOURCE_LOCAL; 548 } else if (manualSync) { 549 source = SyncStorageEngine.SOURCE_USER; 550 } else if (requestedAuthority == null) { 551 source = SyncStorageEngine.SOURCE_POLL; 552 } else { 553 // this isn't strictly server, since arbitrary callers can (and do) request 554 // a non-forced two-way sync on a specific url 555 source = SyncStorageEngine.SOURCE_SERVER; 556 } 557 558 for (AccountAndUser account : accounts) { 559 // Compile a list of authorities that have sync adapters. 560 // For each authority sync each account that matches a sync adapter. 561 final HashSet<String> syncableAuthorities = new HashSet<String>(); 562 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter : 563 mSyncAdapters.getAllServices(account.userId)) { 564 syncableAuthorities.add(syncAdapter.type.authority); 565 } 566 567 // if the url was specified then replace the list of authorities 568 // with just this authority or clear it if this authority isn't 569 // syncable 570 if (requestedAuthority != null) { 571 final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority); 572 syncableAuthorities.clear(); 573 if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority); 574 } 575 576 for (String authority : syncableAuthorities) { 577 int isSyncable = mSyncStorageEngine.getIsSyncable(account.account, account.userId, 578 authority); 579 if (isSyncable == 0) { 580 continue; 581 } 582 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; 583 syncAdapterInfo = mSyncAdapters.getServiceInfo( 584 SyncAdapterType.newKey(authority, account.account.type), account.userId); 585 if (syncAdapterInfo == null) { 586 continue; 587 } 588 final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs(); 589 final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable(); 590 if (isSyncable < 0 && isAlwaysSyncable) { 591 mSyncStorageEngine.setIsSyncable(account.account, account.userId, authority, 1); 592 isSyncable = 1; 593 } 594 if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) { 595 continue; 596 } 597 if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) { 598 continue; 599 } 600 601 // always allow if the isSyncable state is unknown 602 boolean syncAllowed = 603 (isSyncable < 0) 604 || ignoreSettings 605 || (backgroundDataUsageAllowed 606 && mSyncStorageEngine.getMasterSyncAutomatically(account.userId) 607 && mSyncStorageEngine.getSyncAutomatically(account.account, 608 account.userId, authority)); 609 if (!syncAllowed) { 610 if (isLoggable) { 611 Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority 612 + " is not allowed, dropping request"); 613 } 614 continue; 615 } 616 617 Pair<Long, Long> backoff = mSyncStorageEngine 618 .getBackoff(account.account, account.userId, authority); 619 long delayUntil = mSyncStorageEngine.getDelayUntilTime(account.account, 620 account.userId, authority); 621 final long backoffTime = backoff != null ? backoff.first : 0; 622 if (isSyncable < 0) { 623 Bundle newExtras = new Bundle(); 624 newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true); 625 if (isLoggable) { 626 Log.v(TAG, "scheduleSync:" 627 + " delay " + delay 628 + ", source " + source 629 + ", account " + account 630 + ", authority " + authority 631 + ", extras " + newExtras); 632 } 633 scheduleSyncOperation( 634 new SyncOperation(account.account, account.userId, source, authority, 635 newExtras, 0, backoffTime, delayUntil, allowParallelSyncs)); 636 } 637 if (!onlyThoseWithUnkownSyncableState) { 638 if (isLoggable) { 639 Log.v(TAG, "scheduleSync:" 640 + " delay " + delay 641 + ", source " + source 642 + ", account " + account 643 + ", authority " + authority 644 + ", extras " + extras); 645 } 646 scheduleSyncOperation( 647 new SyncOperation(account.account, account.userId, source, authority, 648 extras, delay, backoffTime, delayUntil, allowParallelSyncs)); 649 } 650 } 651 } 652 } 653 654 public void scheduleLocalSync(Account account, int userId, String authority) { 655 final Bundle extras = new Bundle(); 656 extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true); 657 scheduleSync(account, userId, authority, extras, LOCAL_SYNC_DELAY, 658 false /* onlyThoseWithUnkownSyncableState */); 659 } 660 661 public SyncAdapterType[] getSyncAdapterTypes(int userId) { 662 final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos; 663 serviceInfos = mSyncAdapters.getAllServices(userId); 664 SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()]; 665 int i = 0; 666 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) { 667 types[i] = serviceInfo.type; 668 ++i; 669 } 670 return types; 671 } 672 673 private void sendSyncAlarmMessage() { 674 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_ALARM"); 675 mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_SYNC_ALARM); 676 } 677 678 private void sendCheckAlarmsMessage() { 679 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CHECK_ALARMS"); 680 mSyncHandler.removeMessages(SyncHandler.MESSAGE_CHECK_ALARMS); 681 mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_CHECK_ALARMS); 682 } 683 684 private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext, 685 SyncResult syncResult) { 686 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_FINISHED"); 687 Message msg = mSyncHandler.obtainMessage(); 688 msg.what = SyncHandler.MESSAGE_SYNC_FINISHED; 689 msg.obj = new SyncHandlerMessagePayload(syncContext, syncResult); 690 mSyncHandler.sendMessage(msg); 691 } 692 693 private void sendCancelSyncsMessage(final Account account, final int userId, 694 final String authority) { 695 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CANCEL"); 696 Message msg = mSyncHandler.obtainMessage(); 697 msg.what = SyncHandler.MESSAGE_CANCEL; 698 msg.obj = Pair.create(account, authority); 699 msg.arg1 = userId; 700 mSyncHandler.sendMessage(msg); 701 } 702 703 class SyncHandlerMessagePayload { 704 public final ActiveSyncContext activeSyncContext; 705 public final SyncResult syncResult; 706 707 SyncHandlerMessagePayload(ActiveSyncContext syncContext, SyncResult syncResult) { 708 this.activeSyncContext = syncContext; 709 this.syncResult = syncResult; 710 } 711 } 712 713 class SyncAlarmIntentReceiver extends BroadcastReceiver { 714 public void onReceive(Context context, Intent intent) { 715 mHandleAlarmWakeLock.acquire(); 716 sendSyncAlarmMessage(); 717 } 718 } 719 720 private void clearBackoffSetting(SyncOperation op) { 721 mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority, 722 SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); 723 synchronized (mSyncQueue) { 724 mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, 0); 725 } 726 } 727 728 private void increaseBackoffSetting(SyncOperation op) { 729 // TODO: Use this function to align it to an already scheduled sync 730 // operation in the specified window 731 final long now = SystemClock.elapsedRealtime(); 732 733 final Pair<Long, Long> previousSettings = 734 mSyncStorageEngine.getBackoff(op.account, op.userId, op.authority); 735 long newDelayInMs = -1; 736 if (previousSettings != null) { 737 // don't increase backoff before current backoff is expired. This will happen for op's 738 // with ignoreBackoff set. 739 if (now < previousSettings.first) { 740 if (Log.isLoggable(TAG, Log.VERBOSE)) { 741 Log.v(TAG, "Still in backoff, do not increase it. " 742 + "Remaining: " + ((previousSettings.first - now) / 1000) + " seconds."); 743 } 744 return; 745 } 746 // Subsequent delays are the double of the previous delay 747 newDelayInMs = previousSettings.second * 2; 748 } 749 if (newDelayInMs <= 0) { 750 // The initial delay is the jitterized INITIAL_SYNC_RETRY_TIME_IN_MS 751 newDelayInMs = jitterize(INITIAL_SYNC_RETRY_TIME_IN_MS, 752 (long)(INITIAL_SYNC_RETRY_TIME_IN_MS * 1.1)); 753 } 754 755 // Cap the delay 756 long maxSyncRetryTimeInSeconds = Settings.Global.getLong(mContext.getContentResolver(), 757 Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS, 758 DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS); 759 if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) { 760 newDelayInMs = maxSyncRetryTimeInSeconds * 1000; 761 } 762 763 final long backoff = now + newDelayInMs; 764 765 mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority, 766 backoff, newDelayInMs); 767 768 op.backoff = backoff; 769 op.updateEffectiveRunTime(); 770 771 synchronized (mSyncQueue) { 772 mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, backoff); 773 } 774 } 775 776 private void setDelayUntilTime(SyncOperation op, long delayUntilSeconds) { 777 final long delayUntil = delayUntilSeconds * 1000; 778 final long absoluteNow = System.currentTimeMillis(); 779 long newDelayUntilTime; 780 if (delayUntil > absoluteNow) { 781 newDelayUntilTime = SystemClock.elapsedRealtime() + (delayUntil - absoluteNow); 782 } else { 783 newDelayUntilTime = 0; 784 } 785 mSyncStorageEngine 786 .setDelayUntilTime(op.account, op.userId, op.authority, newDelayUntilTime); 787 synchronized (mSyncQueue) { 788 mSyncQueue.onDelayUntilTimeChanged(op.account, op.authority, newDelayUntilTime); 789 } 790 } 791 792 /** 793 * Cancel the active sync if it matches the authority and account. 794 * @param account limit the cancelations to syncs with this account, if non-null 795 * @param authority limit the cancelations to syncs with this authority, if non-null 796 */ 797 public void cancelActiveSync(Account account, int userId, String authority) { 798 sendCancelSyncsMessage(account, userId, authority); 799 } 800 801 /** 802 * Create and schedule a SyncOperation. 803 * 804 * @param syncOperation the SyncOperation to schedule 805 */ 806 public void scheduleSyncOperation(SyncOperation syncOperation) { 807 boolean queueChanged; 808 synchronized (mSyncQueue) { 809 queueChanged = mSyncQueue.add(syncOperation); 810 } 811 812 if (queueChanged) { 813 if (Log.isLoggable(TAG, Log.VERBOSE)) { 814 Log.v(TAG, "scheduleSyncOperation: enqueued " + syncOperation); 815 } 816 sendCheckAlarmsMessage(); 817 } else { 818 if (Log.isLoggable(TAG, Log.VERBOSE)) { 819 Log.v(TAG, "scheduleSyncOperation: dropping duplicate sync operation " 820 + syncOperation); 821 } 822 } 823 } 824 825 /** 826 * Remove scheduled sync operations. 827 * @param account limit the removals to operations with this account, if non-null 828 * @param authority limit the removals to operations with this authority, if non-null 829 */ 830 public void clearScheduledSyncOperations(Account account, int userId, String authority) { 831 synchronized (mSyncQueue) { 832 mSyncQueue.remove(account, userId, authority); 833 } 834 mSyncStorageEngine.setBackoff(account, userId, authority, 835 SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); 836 } 837 838 void maybeRescheduleSync(SyncResult syncResult, SyncOperation operation) { 839 boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG); 840 if (isLoggable) { 841 Log.d(TAG, "encountered error(s) during the sync: " + syncResult + ", " + operation); 842 } 843 844 operation = new SyncOperation(operation); 845 846 // The SYNC_EXTRAS_IGNORE_BACKOFF only applies to the first attempt to sync a given 847 // request. Retries of the request will always honor the backoff, so clear the 848 // flag in case we retry this request. 849 if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) { 850 operation.extras.remove(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF); 851 } 852 853 // If this sync aborted because the internal sync loop retried too many times then 854 // don't reschedule. Otherwise we risk getting into a retry loop. 855 // If the operation succeeded to some extent then retry immediately. 856 // If this was a two-way sync then retry soft errors with an exponential backoff. 857 // If this was an upward sync then schedule a two-way sync immediately. 858 // Otherwise do not reschedule. 859 if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)) { 860 Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified " 861 + operation); 862 } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false) 863 && !syncResult.syncAlreadyInProgress) { 864 operation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD); 865 Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync " 866 + "encountered an error: " + operation); 867 scheduleSyncOperation(operation); 868 } else if (syncResult.tooManyRetries) { 869 Log.d(TAG, "not retrying sync operation because it retried too many times: " 870 + operation); 871 } else if (syncResult.madeSomeProgress()) { 872 if (isLoggable) { 873 Log.d(TAG, "retrying sync operation because even though it had an error " 874 + "it achieved some success"); 875 } 876 scheduleSyncOperation(operation); 877 } else if (syncResult.syncAlreadyInProgress) { 878 if (isLoggable) { 879 Log.d(TAG, "retrying sync operation that failed because there was already a " 880 + "sync in progress: " + operation); 881 } 882 scheduleSyncOperation(new SyncOperation(operation.account, operation.userId, 883 operation.syncSource, 884 operation.authority, operation.extras, 885 DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000, 886 operation.backoff, operation.delayUntil, operation.allowParallelSyncs)); 887 } else if (syncResult.hasSoftError()) { 888 if (isLoggable) { 889 Log.d(TAG, "retrying sync operation because it encountered a soft error: " 890 + operation); 891 } 892 scheduleSyncOperation(operation); 893 } else { 894 Log.d(TAG, "not retrying sync operation because the error is a hard error: " 895 + operation); 896 } 897 } 898 899 private void onUserStarting(int userId) { 900 // Make sure that accounts we're about to use are valid 901 AccountManagerService.getSingleton().validateAccounts(userId); 902 903 mSyncAdapters.invalidateCache(userId); 904 905 updateRunningAccounts(); 906 907 synchronized (mSyncQueue) { 908 mSyncQueue.addPendingOperations(userId); 909 } 910 911 // Schedule sync for any accounts under started user 912 final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId); 913 for (Account account : accounts) { 914 scheduleSync(account, userId, null, null, 0 /* no delay */, 915 true /* onlyThoseWithUnknownSyncableState */); 916 } 917 918 sendCheckAlarmsMessage(); 919 } 920 921 private void onUserStopping(int userId) { 922 updateRunningAccounts(); 923 924 cancelActiveSync( 925 null /* any account */, 926 userId, 927 null /* any authority */); 928 } 929 930 private void onUserRemoved(int userId) { 931 updateRunningAccounts(); 932 933 // Clean up the storage engine database 934 mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId); 935 synchronized (mSyncQueue) { 936 mSyncQueue.removeUser(userId); 937 } 938 } 939 940 /** 941 * @hide 942 */ 943 class ActiveSyncContext extends ISyncContext.Stub 944 implements ServiceConnection, IBinder.DeathRecipient { 945 final SyncOperation mSyncOperation; 946 final long mHistoryRowId; 947 ISyncAdapter mSyncAdapter; 948 final long mStartTime; 949 long mTimeoutStartTime; 950 boolean mBound; 951 final PowerManager.WakeLock mSyncWakeLock; 952 final int mSyncAdapterUid; 953 SyncInfo mSyncInfo; 954 boolean mIsLinkedToDeath = false; 955 956 /** 957 * Create an ActiveSyncContext for an impending sync and grab the wakelock for that 958 * sync adapter. Since this grabs the wakelock you need to be sure to call 959 * close() when you are done with this ActiveSyncContext, whether the sync succeeded 960 * or not. 961 * @param syncOperation the SyncOperation we are about to sync 962 * @param historyRowId the row in which to record the history info for this sync 963 * @param syncAdapterUid the UID of the application that contains the sync adapter 964 * for this sync. This is used to attribute the wakelock hold to that application. 965 */ 966 public ActiveSyncContext(SyncOperation syncOperation, long historyRowId, 967 int syncAdapterUid) { 968 super(); 969 mSyncAdapterUid = syncAdapterUid; 970 mSyncOperation = syncOperation; 971 mHistoryRowId = historyRowId; 972 mSyncAdapter = null; 973 mStartTime = SystemClock.elapsedRealtime(); 974 mTimeoutStartTime = mStartTime; 975 mSyncWakeLock = mSyncHandler.getSyncWakeLock( 976 mSyncOperation.account, mSyncOperation.authority); 977 mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterUid)); 978 mSyncWakeLock.acquire(); 979 } 980 981 public void sendHeartbeat() { 982 // heartbeats are no longer used 983 } 984 985 public void onFinished(SyncResult result) { 986 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "onFinished: " + this); 987 // include "this" in the message so that the handler can ignore it if this 988 // ActiveSyncContext is no longer the mActiveSyncContext at message handling 989 // time 990 sendSyncFinishedOrCanceledMessage(this, result); 991 } 992 993 public void toString(StringBuilder sb) { 994 sb.append("startTime ").append(mStartTime) 995 .append(", mTimeoutStartTime ").append(mTimeoutStartTime) 996 .append(", mHistoryRowId ").append(mHistoryRowId) 997 .append(", syncOperation ").append(mSyncOperation); 998 } 999 1000 public void onServiceConnected(ComponentName name, IBinder service) { 1001 Message msg = mSyncHandler.obtainMessage(); 1002 msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED; 1003 msg.obj = new ServiceConnectionData(this, ISyncAdapter.Stub.asInterface(service)); 1004 mSyncHandler.sendMessage(msg); 1005 } 1006 1007 public void onServiceDisconnected(ComponentName name) { 1008 Message msg = mSyncHandler.obtainMessage(); 1009 msg.what = SyncHandler.MESSAGE_SERVICE_DISCONNECTED; 1010 msg.obj = new ServiceConnectionData(this, null); 1011 mSyncHandler.sendMessage(msg); 1012 } 1013 1014 boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info, int userId) { 1015 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1016 Log.d(TAG, "bindToSyncAdapter: " + info.componentName + ", connection " + this); 1017 } 1018 Intent intent = new Intent(); 1019 intent.setAction("android.content.SyncAdapter"); 1020 intent.setComponent(info.componentName); 1021 intent.putExtra(Intent.EXTRA_CLIENT_LABEL, 1022 com.android.internal.R.string.sync_binding_label); 1023 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser( 1024 mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0, 1025 null, new UserHandle(userId))); 1026 mBound = true; 1027 final boolean bindResult = mContext.bindService(intent, this, 1028 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND 1029 | Context.BIND_ALLOW_OOM_MANAGEMENT, 1030 mSyncOperation.userId); 1031 if (!bindResult) { 1032 mBound = false; 1033 } 1034 return bindResult; 1035 } 1036 1037 /** 1038 * Performs the required cleanup, which is the releasing of the wakelock and 1039 * unbinding from the sync adapter (if actually bound). 1040 */ 1041 protected void close() { 1042 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1043 Log.d(TAG, "unBindFromSyncAdapter: connection " + this); 1044 } 1045 if (mBound) { 1046 mBound = false; 1047 mContext.unbindService(this); 1048 } 1049 mSyncWakeLock.release(); 1050 mSyncWakeLock.setWorkSource(null); 1051 } 1052 1053 @Override 1054 public String toString() { 1055 StringBuilder sb = new StringBuilder(); 1056 toString(sb); 1057 return sb.toString(); 1058 } 1059 1060 @Override 1061 public void binderDied() { 1062 sendSyncFinishedOrCanceledMessage(this, null); 1063 } 1064 } 1065 1066 protected void dump(FileDescriptor fd, PrintWriter pw) { 1067 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 1068 dumpSyncState(ipw); 1069 dumpSyncHistory(ipw); 1070 dumpSyncAdapters(ipw); 1071 } 1072 1073 static String formatTime(long time) { 1074 Time tobj = new Time(); 1075 tobj.set(time); 1076 return tobj.format("%Y-%m-%d %H:%M:%S"); 1077 } 1078 1079 protected void dumpSyncState(PrintWriter pw) { 1080 pw.print("data connected: "); pw.println(mDataConnectionIsConnected); 1081 pw.print("auto sync: "); 1082 List<UserInfo> users = getAllUsers(); 1083 if (users != null) { 1084 for (UserInfo user : users) { 1085 pw.print("u" + user.id + "=" 1086 + mSyncStorageEngine.getMasterSyncAutomatically(user.id) + " "); 1087 } 1088 pw.println(); 1089 } 1090 pw.print("memory low: "); pw.println(mStorageIsLow); 1091 1092 final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts(); 1093 1094 pw.print("accounts: "); 1095 if (accounts != INITIAL_ACCOUNTS_ARRAY) { 1096 pw.println(accounts.length); 1097 } else { 1098 pw.println("not known yet"); 1099 } 1100 final long now = SystemClock.elapsedRealtime(); 1101 pw.print("now: "); pw.print(now); 1102 pw.println(" (" + formatTime(System.currentTimeMillis()) + ")"); 1103 pw.print("offset: "); pw.print(DateUtils.formatElapsedTime(mSyncRandomOffsetMillis/1000)); 1104 pw.println(" (HH:MM:SS)"); 1105 pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now/1000)); 1106 pw.println(" (HH:MM:SS)"); 1107 pw.print("time spent syncing: "); 1108 pw.print(DateUtils.formatElapsedTime( 1109 mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000)); 1110 pw.print(" (HH:MM:SS), sync "); 1111 pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not "); 1112 pw.println("in progress"); 1113 if (mSyncHandler.mAlarmScheduleTime != null) { 1114 pw.print("next alarm time: "); pw.print(mSyncHandler.mAlarmScheduleTime); 1115 pw.print(" ("); 1116 pw.print(DateUtils.formatElapsedTime((mSyncHandler.mAlarmScheduleTime-now)/1000)); 1117 pw.println(" (HH:MM:SS) from now)"); 1118 } else { 1119 pw.println("no alarm is scheduled (there had better not be any pending syncs)"); 1120 } 1121 1122 pw.print("notification info: "); 1123 final StringBuilder sb = new StringBuilder(); 1124 mSyncHandler.mSyncNotificationInfo.toString(sb); 1125 pw.println(sb.toString()); 1126 1127 pw.println(); 1128 pw.println("Active Syncs: " + mActiveSyncContexts.size()); 1129 for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) { 1130 final long durationInSeconds = (now - activeSyncContext.mStartTime) / 1000; 1131 pw.print(" "); 1132 pw.print(DateUtils.formatElapsedTime(durationInSeconds)); 1133 pw.print(" - "); 1134 pw.print(activeSyncContext.mSyncOperation.dump(false)); 1135 pw.println(); 1136 } 1137 1138 synchronized (mSyncQueue) { 1139 sb.setLength(0); 1140 mSyncQueue.dump(sb); 1141 } 1142 pw.println(); 1143 pw.print(sb.toString()); 1144 1145 // join the installed sync adapter with the accounts list and emit for everything 1146 pw.println(); 1147 pw.println("Sync Status"); 1148 for (AccountAndUser account : accounts) { 1149 pw.print(" Account "); pw.print(account.account.name); 1150 pw.print(" u"); pw.print(account.userId); 1151 pw.print(" "); pw.print(account.account.type); 1152 pw.println(":"); 1153 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : 1154 mSyncAdapters.getAllServices(account.userId)) { 1155 if (!syncAdapterType.type.accountType.equals(account.account.type)) { 1156 continue; 1157 } 1158 1159 SyncStorageEngine.AuthorityInfo settings = 1160 mSyncStorageEngine.getOrCreateAuthority( 1161 account.account, account.userId, syncAdapterType.type.authority); 1162 SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(settings); 1163 pw.print(" "); pw.print(settings.authority); 1164 pw.println(":"); 1165 pw.print(" settings:"); 1166 pw.print(" " + (settings.syncable > 0 1167 ? "syncable" 1168 : (settings.syncable == 0 ? "not syncable" : "not initialized"))); 1169 pw.print(", " + (settings.enabled ? "enabled" : "disabled")); 1170 if (settings.delayUntil > now) { 1171 pw.print(", delay for " 1172 + ((settings.delayUntil - now) / 1000) + " sec"); 1173 } 1174 if (settings.backoffTime > now) { 1175 pw.print(", backoff for " 1176 + ((settings.backoffTime - now) / 1000) + " sec"); 1177 } 1178 if (settings.backoffDelay > 0) { 1179 pw.print(", the backoff increment is " + settings.backoffDelay / 1000 1180 + " sec"); 1181 } 1182 pw.println(); 1183 for (int periodicIndex = 0; 1184 periodicIndex < settings.periodicSyncs.size(); 1185 periodicIndex++) { 1186 Pair<Bundle, Long> info = settings.periodicSyncs.get(periodicIndex); 1187 long lastPeriodicTime = status.getPeriodicSyncTime(periodicIndex); 1188 long nextPeriodicTime = lastPeriodicTime + info.second * 1000; 1189 pw.println(" periodic period=" + info.second 1190 + ", extras=" + info.first 1191 + ", next=" + formatTime(nextPeriodicTime)); 1192 } 1193 pw.print(" count: local="); pw.print(status.numSourceLocal); 1194 pw.print(" poll="); pw.print(status.numSourcePoll); 1195 pw.print(" periodic="); pw.print(status.numSourcePeriodic); 1196 pw.print(" server="); pw.print(status.numSourceServer); 1197 pw.print(" user="); pw.print(status.numSourceUser); 1198 pw.print(" total="); pw.print(status.numSyncs); 1199 pw.println(); 1200 pw.print(" total duration: "); 1201 pw.println(DateUtils.formatElapsedTime(status.totalElapsedTime/1000)); 1202 if (status.lastSuccessTime != 0) { 1203 pw.print(" SUCCESS: source="); 1204 pw.print(SyncStorageEngine.SOURCES[status.lastSuccessSource]); 1205 pw.print(" time="); 1206 pw.println(formatTime(status.lastSuccessTime)); 1207 } 1208 if (status.lastFailureTime != 0) { 1209 pw.print(" FAILURE: source="); 1210 pw.print(SyncStorageEngine.SOURCES[ 1211 status.lastFailureSource]); 1212 pw.print(" initialTime="); 1213 pw.print(formatTime(status.initialFailureTime)); 1214 pw.print(" lastTime="); 1215 pw.println(formatTime(status.lastFailureTime)); 1216 int errCode = status.getLastFailureMesgAsInt(0); 1217 pw.print(" message: "); pw.println( 1218 getLastFailureMessage(errCode) + " (" + errCode + ")"); 1219 } 1220 } 1221 } 1222 } 1223 1224 private String getLastFailureMessage(int code) { 1225 switch (code) { 1226 case ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS: 1227 return "sync already in progress"; 1228 1229 case ContentResolver.SYNC_ERROR_AUTHENTICATION: 1230 return "authentication error"; 1231 1232 case ContentResolver.SYNC_ERROR_IO: 1233 return "I/O error"; 1234 1235 case ContentResolver.SYNC_ERROR_PARSE: 1236 return "parse error"; 1237 1238 case ContentResolver.SYNC_ERROR_CONFLICT: 1239 return "conflict error"; 1240 1241 case ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS: 1242 return "too many deletions error"; 1243 1244 case ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES: 1245 return "too many retries error"; 1246 1247 case ContentResolver.SYNC_ERROR_INTERNAL: 1248 return "internal error"; 1249 1250 default: 1251 return "unknown"; 1252 } 1253 } 1254 1255 private void dumpTimeSec(PrintWriter pw, long time) { 1256 pw.print(time/1000); pw.print('.'); pw.print((time/100)%10); 1257 pw.print('s'); 1258 } 1259 1260 private void dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds) { 1261 pw.print("Success ("); pw.print(ds.successCount); 1262 if (ds.successCount > 0) { 1263 pw.print(" for "); dumpTimeSec(pw, ds.successTime); 1264 pw.print(" avg="); dumpTimeSec(pw, ds.successTime/ds.successCount); 1265 } 1266 pw.print(") Failure ("); pw.print(ds.failureCount); 1267 if (ds.failureCount > 0) { 1268 pw.print(" for "); dumpTimeSec(pw, ds.failureTime); 1269 pw.print(" avg="); dumpTimeSec(pw, ds.failureTime/ds.failureCount); 1270 } 1271 pw.println(")"); 1272 } 1273 1274 protected void dumpSyncHistory(PrintWriter pw) { 1275 dumpRecentHistory(pw); 1276 dumpDayStatistics(pw); 1277 } 1278 1279 private void dumpRecentHistory(PrintWriter pw) { 1280 final ArrayList<SyncStorageEngine.SyncHistoryItem> items 1281 = mSyncStorageEngine.getSyncHistory(); 1282 if (items != null && items.size() > 0) { 1283 final Map<String, AuthoritySyncStats> authorityMap = Maps.newHashMap(); 1284 long totalElapsedTime = 0; 1285 long totalTimes = 0; 1286 final int N = items.size(); 1287 1288 int maxAuthority = 0; 1289 int maxAccount = 0; 1290 for (SyncStorageEngine.SyncHistoryItem item : items) { 1291 SyncStorageEngine.AuthorityInfo authority 1292 = mSyncStorageEngine.getAuthority(item.authorityId); 1293 final String authorityName; 1294 final String accountKey; 1295 if (authority != null) { 1296 authorityName = authority.authority; 1297 accountKey = authority.account.name + "/" + authority.account.type 1298 + " u" + authority.userId; 1299 } else { 1300 authorityName = "Unknown"; 1301 accountKey = "Unknown"; 1302 } 1303 1304 int length = authorityName.length(); 1305 if (length > maxAuthority) { 1306 maxAuthority = length; 1307 } 1308 length = accountKey.length(); 1309 if (length > maxAccount) { 1310 maxAccount = length; 1311 } 1312 1313 final long elapsedTime = item.elapsedTime; 1314 totalElapsedTime += elapsedTime; 1315 totalTimes++; 1316 AuthoritySyncStats authoritySyncStats = authorityMap.get(authorityName); 1317 if (authoritySyncStats == null) { 1318 authoritySyncStats = new AuthoritySyncStats(authorityName); 1319 authorityMap.put(authorityName, authoritySyncStats); 1320 } 1321 authoritySyncStats.elapsedTime += elapsedTime; 1322 authoritySyncStats.times++; 1323 final Map<String, AccountSyncStats> accountMap = authoritySyncStats.accountMap; 1324 AccountSyncStats accountSyncStats = accountMap.get(accountKey); 1325 if (accountSyncStats == null) { 1326 accountSyncStats = new AccountSyncStats(accountKey); 1327 accountMap.put(accountKey, accountSyncStats); 1328 } 1329 accountSyncStats.elapsedTime += elapsedTime; 1330 accountSyncStats.times++; 1331 1332 } 1333 1334 if (totalElapsedTime > 0) { 1335 pw.println(); 1336 pw.printf("Detailed Statistics (Recent history): " 1337 + "%d (# of times) %ds (sync time)\n", 1338 totalTimes, totalElapsedTime / 1000); 1339 1340 final List<AuthoritySyncStats> sortedAuthorities = 1341 new ArrayList<AuthoritySyncStats>(authorityMap.values()); 1342 Collections.sort(sortedAuthorities, new Comparator<AuthoritySyncStats>() { 1343 @Override 1344 public int compare(AuthoritySyncStats lhs, AuthoritySyncStats rhs) { 1345 // reverse order 1346 int compare = Integer.compare(rhs.times, lhs.times); 1347 if (compare == 0) { 1348 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime); 1349 } 1350 return compare; 1351 } 1352 }); 1353 1354 final int maxLength = Math.max(maxAuthority, maxAccount + 3); 1355 final int padLength = 2 + 2 + maxLength + 2 + 10 + 11; 1356 final char chars[] = new char[padLength]; 1357 Arrays.fill(chars, '-'); 1358 final String separator = new String(chars); 1359 1360 final String authorityFormat = 1361 String.format(" %%-%ds: %%-9s %%-11s\n", maxLength + 2); 1362 final String accountFormat = 1363 String.format(" %%-%ds: %%-9s %%-11s\n", maxLength); 1364 1365 pw.println(separator); 1366 for (AuthoritySyncStats authoritySyncStats : sortedAuthorities) { 1367 String name = authoritySyncStats.name; 1368 long elapsedTime; 1369 int times; 1370 String timeStr; 1371 String timesStr; 1372 1373 elapsedTime = authoritySyncStats.elapsedTime; 1374 times = authoritySyncStats.times; 1375 timeStr = String.format("%ds/%d%%", 1376 elapsedTime / 1000, 1377 elapsedTime * 100 / totalElapsedTime); 1378 timesStr = String.format("%d/%d%%", 1379 times, 1380 times * 100 / totalTimes); 1381 pw.printf(authorityFormat, name, timesStr, timeStr); 1382 1383 final List<AccountSyncStats> sortedAccounts = 1384 new ArrayList<AccountSyncStats>( 1385 authoritySyncStats.accountMap.values()); 1386 Collections.sort(sortedAccounts, new Comparator<AccountSyncStats>() { 1387 @Override 1388 public int compare(AccountSyncStats lhs, AccountSyncStats rhs) { 1389 // reverse order 1390 int compare = Integer.compare(rhs.times, lhs.times); 1391 if (compare == 0) { 1392 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime); 1393 } 1394 return compare; 1395 } 1396 }); 1397 for (AccountSyncStats stats: sortedAccounts) { 1398 elapsedTime = stats.elapsedTime; 1399 times = stats.times; 1400 timeStr = String.format("%ds/%d%%", 1401 elapsedTime / 1000, 1402 elapsedTime * 100 / totalElapsedTime); 1403 timesStr = String.format("%d/%d%%", 1404 times, 1405 times * 100 / totalTimes); 1406 pw.printf(accountFormat, stats.name, timesStr, timeStr); 1407 } 1408 pw.println(separator); 1409 } 1410 } 1411 1412 pw.println(); 1413 pw.println("Recent Sync History"); 1414 final String format = " %-" + maxAccount + "s %s\n"; 1415 final Map<String, Long> lastTimeMap = Maps.newHashMap(); 1416 1417 for (int i = 0; i < N; i++) { 1418 SyncStorageEngine.SyncHistoryItem item = items.get(i); 1419 SyncStorageEngine.AuthorityInfo authority 1420 = mSyncStorageEngine.getAuthority(item.authorityId); 1421 final String authorityName; 1422 final String accountKey; 1423 if (authority != null) { 1424 authorityName = authority.authority; 1425 accountKey = authority.account.name + "/" + authority.account.type 1426 + " u" + authority.userId; 1427 } else { 1428 authorityName = "Unknown"; 1429 accountKey = "Unknown"; 1430 } 1431 final long elapsedTime = item.elapsedTime; 1432 final Time time = new Time(); 1433 final long eventTime = item.eventTime; 1434 time.set(eventTime); 1435 1436 final String key = authorityName + "/" + accountKey; 1437 final Long lastEventTime = lastTimeMap.get(key); 1438 final String diffString; 1439 if (lastEventTime == null) { 1440 diffString = ""; 1441 } else { 1442 final long diff = (lastEventTime - eventTime) / 1000; 1443 if (diff < 60) { 1444 diffString = String.valueOf(diff); 1445 } else if (diff < 3600) { 1446 diffString = String.format("%02d:%02d", diff / 60, diff % 60); 1447 } else { 1448 final long sec = diff % 3600; 1449 diffString = String.format("%02d:%02d:%02d", 1450 diff / 3600, sec / 60, sec % 60); 1451 } 1452 } 1453 lastTimeMap.put(key, eventTime); 1454 1455 pw.printf(" #%-3d: %s %8s %5.1fs %8s", 1456 i + 1, 1457 formatTime(eventTime), 1458 SyncStorageEngine.SOURCES[item.source], 1459 ((float) elapsedTime) / 1000, 1460 diffString); 1461 pw.printf(format, accountKey, authorityName); 1462 1463 if (item.event != SyncStorageEngine.EVENT_STOP 1464 || item.upstreamActivity != 0 1465 || item.downstreamActivity != 0) { 1466 pw.printf(" event=%d upstreamActivity=%d downstreamActivity=%d\n", 1467 item.event, 1468 item.upstreamActivity, 1469 item.downstreamActivity); 1470 } 1471 if (item.mesg != null 1472 && !SyncStorageEngine.MESG_SUCCESS.equals(item.mesg)) { 1473 pw.printf(" mesg=%s\n", item.mesg); 1474 } 1475 } 1476 } 1477 } 1478 1479 private void dumpDayStatistics(PrintWriter pw) { 1480 SyncStorageEngine.DayStats dses[] = mSyncStorageEngine.getDayStatistics(); 1481 if (dses != null && dses[0] != null) { 1482 pw.println(); 1483 pw.println("Sync Statistics"); 1484 pw.print(" Today: "); dumpDayStatistic(pw, dses[0]); 1485 int today = dses[0].day; 1486 int i; 1487 SyncStorageEngine.DayStats ds; 1488 1489 // Print each day in the current week. 1490 for (i=1; i<=6 && i < dses.length; i++) { 1491 ds = dses[i]; 1492 if (ds == null) break; 1493 int delta = today-ds.day; 1494 if (delta > 6) break; 1495 1496 pw.print(" Day-"); pw.print(delta); pw.print(": "); 1497 dumpDayStatistic(pw, ds); 1498 } 1499 1500 // Aggregate all following days into weeks and print totals. 1501 int weekDay = today; 1502 while (i < dses.length) { 1503 SyncStorageEngine.DayStats aggr = null; 1504 weekDay -= 7; 1505 while (i < dses.length) { 1506 ds = dses[i]; 1507 if (ds == null) { 1508 i = dses.length; 1509 break; 1510 } 1511 int delta = weekDay-ds.day; 1512 if (delta > 6) break; 1513 i++; 1514 1515 if (aggr == null) { 1516 aggr = new SyncStorageEngine.DayStats(weekDay); 1517 } 1518 aggr.successCount += ds.successCount; 1519 aggr.successTime += ds.successTime; 1520 aggr.failureCount += ds.failureCount; 1521 aggr.failureTime += ds.failureTime; 1522 } 1523 if (aggr != null) { 1524 pw.print(" Week-"); pw.print((today-weekDay)/7); pw.print(": "); 1525 dumpDayStatistic(pw, aggr); 1526 } 1527 } 1528 } 1529 } 1530 1531 private void dumpSyncAdapters(IndentingPrintWriter pw) { 1532 pw.println(); 1533 final List<UserInfo> users = getAllUsers(); 1534 if (users != null) { 1535 for (UserInfo user : users) { 1536 pw.println("Sync adapters for " + user + ":"); 1537 pw.increaseIndent(); 1538 for (RegisteredServicesCache.ServiceInfo<?> info : 1539 mSyncAdapters.getAllServices(user.id)) { 1540 pw.println(info); 1541 } 1542 pw.decreaseIndent(); 1543 pw.println(); 1544 } 1545 } 1546 } 1547 1548 private static class AuthoritySyncStats { 1549 String name; 1550 long elapsedTime; 1551 int times; 1552 Map<String, AccountSyncStats> accountMap = Maps.newHashMap(); 1553 1554 private AuthoritySyncStats(String name) { 1555 this.name = name; 1556 } 1557 } 1558 1559 private static class AccountSyncStats { 1560 String name; 1561 long elapsedTime; 1562 int times; 1563 1564 private AccountSyncStats(String name) { 1565 this.name = name; 1566 } 1567 } 1568 1569 /** 1570 * A helper object to keep track of the time we have spent syncing since the last boot 1571 */ 1572 private class SyncTimeTracker { 1573 /** True if a sync was in progress on the most recent call to update() */ 1574 boolean mLastWasSyncing = false; 1575 /** Used to track when lastWasSyncing was last set */ 1576 long mWhenSyncStarted = 0; 1577 /** The cumulative time we have spent syncing */ 1578 private long mTimeSpentSyncing; 1579 1580 /** Call to let the tracker know that the sync state may have changed */ 1581 public synchronized void update() { 1582 final boolean isSyncInProgress = !mActiveSyncContexts.isEmpty(); 1583 if (isSyncInProgress == mLastWasSyncing) return; 1584 final long now = SystemClock.elapsedRealtime(); 1585 if (isSyncInProgress) { 1586 mWhenSyncStarted = now; 1587 } else { 1588 mTimeSpentSyncing += now - mWhenSyncStarted; 1589 } 1590 mLastWasSyncing = isSyncInProgress; 1591 } 1592 1593 /** Get how long we have been syncing, in ms */ 1594 public synchronized long timeSpentSyncing() { 1595 if (!mLastWasSyncing) return mTimeSpentSyncing; 1596 1597 final long now = SystemClock.elapsedRealtime(); 1598 return mTimeSpentSyncing + (now - mWhenSyncStarted); 1599 } 1600 } 1601 1602 class ServiceConnectionData { 1603 public final ActiveSyncContext activeSyncContext; 1604 public final ISyncAdapter syncAdapter; 1605 ServiceConnectionData(ActiveSyncContext activeSyncContext, ISyncAdapter syncAdapter) { 1606 this.activeSyncContext = activeSyncContext; 1607 this.syncAdapter = syncAdapter; 1608 } 1609 } 1610 1611 /** 1612 * Handles SyncOperation Messages that are posted to the associated 1613 * HandlerThread. 1614 */ 1615 class SyncHandler extends Handler { 1616 // Messages that can be sent on mHandler 1617 private static final int MESSAGE_SYNC_FINISHED = 1; 1618 private static final int MESSAGE_SYNC_ALARM = 2; 1619 private static final int MESSAGE_CHECK_ALARMS = 3; 1620 private static final int MESSAGE_SERVICE_CONNECTED = 4; 1621 private static final int MESSAGE_SERVICE_DISCONNECTED = 5; 1622 private static final int MESSAGE_CANCEL = 6; 1623 1624 public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo(); 1625 private Long mAlarmScheduleTime = null; 1626 public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker(); 1627 private final HashMap<Pair<Account, String>, PowerManager.WakeLock> mWakeLocks = 1628 Maps.newHashMap(); 1629 1630 private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1); 1631 1632 public void onBootCompleted() { 1633 mBootCompleted = true; 1634 1635 doDatabaseCleanup(); 1636 1637 if (mReadyToRunLatch != null) { 1638 mReadyToRunLatch.countDown(); 1639 } 1640 } 1641 1642 private PowerManager.WakeLock getSyncWakeLock(Account account, String authority) { 1643 final Pair<Account, String> wakeLockKey = Pair.create(account, authority); 1644 PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey); 1645 if (wakeLock == null) { 1646 final String name = SYNC_WAKE_LOCK_PREFIX + "_" + authority + "_" + account; 1647 wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name); 1648 wakeLock.setReferenceCounted(false); 1649 mWakeLocks.put(wakeLockKey, wakeLock); 1650 } 1651 return wakeLock; 1652 } 1653 1654 private void waitUntilReadyToRun() { 1655 CountDownLatch latch = mReadyToRunLatch; 1656 if (latch != null) { 1657 while (true) { 1658 try { 1659 latch.await(); 1660 mReadyToRunLatch = null; 1661 return; 1662 } catch (InterruptedException e) { 1663 Thread.currentThread().interrupt(); 1664 } 1665 } 1666 } 1667 } 1668 /** 1669 * Used to keep track of whether a sync notification is active and who it is for. 1670 */ 1671 class SyncNotificationInfo { 1672 // true iff the notification manager has been asked to send the notification 1673 public boolean isActive = false; 1674 1675 // Set when we transition from not running a sync to running a sync, and cleared on 1676 // the opposite transition. 1677 public Long startTime = null; 1678 1679 public void toString(StringBuilder sb) { 1680 sb.append("isActive ").append(isActive).append(", startTime ").append(startTime); 1681 } 1682 1683 @Override 1684 public String toString() { 1685 StringBuilder sb = new StringBuilder(); 1686 toString(sb); 1687 return sb.toString(); 1688 } 1689 } 1690 1691 public SyncHandler(Looper looper) { 1692 super(looper); 1693 } 1694 1695 public void handleMessage(Message msg) { 1696 long earliestFuturePollTime = Long.MAX_VALUE; 1697 long nextPendingSyncTime = Long.MAX_VALUE; 1698 1699 // Setting the value here instead of a method because we want the dumpsys logs 1700 // to have the most recent value used. 1701 try { 1702 waitUntilReadyToRun(); 1703 mDataConnectionIsConnected = readDataConnectionState(); 1704 mSyncManagerWakeLock.acquire(); 1705 // Always do this first so that we be sure that any periodic syncs that 1706 // are ready to run have been converted into pending syncs. This allows the 1707 // logic that considers the next steps to take based on the set of pending syncs 1708 // to also take into account the periodic syncs. 1709 earliestFuturePollTime = scheduleReadyPeriodicSyncs(); 1710 switch (msg.what) { 1711 case SyncHandler.MESSAGE_CANCEL: { 1712 Pair<Account, String> payload = (Pair<Account, String>)msg.obj; 1713 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1714 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CANCEL: " 1715 + payload.first + ", " + payload.second); 1716 } 1717 cancelActiveSyncLocked(payload.first, msg.arg1, payload.second); 1718 nextPendingSyncTime = maybeStartNextSyncLocked(); 1719 break; 1720 } 1721 1722 case SyncHandler.MESSAGE_SYNC_FINISHED: 1723 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1724 Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_FINISHED"); 1725 } 1726 SyncHandlerMessagePayload payload = (SyncHandlerMessagePayload)msg.obj; 1727 if (!isSyncStillActive(payload.activeSyncContext)) { 1728 Log.d(TAG, "handleSyncHandlerMessage: dropping since the " 1729 + "sync is no longer active: " 1730 + payload.activeSyncContext); 1731 break; 1732 } 1733 runSyncFinishedOrCanceledLocked(payload.syncResult, payload.activeSyncContext); 1734 1735 // since a sync just finished check if it is time to start a new sync 1736 nextPendingSyncTime = maybeStartNextSyncLocked(); 1737 break; 1738 1739 case SyncHandler.MESSAGE_SERVICE_CONNECTED: { 1740 ServiceConnectionData msgData = (ServiceConnectionData)msg.obj; 1741 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1742 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: " 1743 + msgData.activeSyncContext); 1744 } 1745 // check that this isn't an old message 1746 if (isSyncStillActive(msgData.activeSyncContext)) { 1747 runBoundToSyncAdapter(msgData.activeSyncContext, msgData.syncAdapter); 1748 } 1749 break; 1750 } 1751 1752 case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: { 1753 final ActiveSyncContext currentSyncContext = 1754 ((ServiceConnectionData)msg.obj).activeSyncContext; 1755 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1756 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: " 1757 + currentSyncContext); 1758 } 1759 // check that this isn't an old message 1760 if (isSyncStillActive(currentSyncContext)) { 1761 // cancel the sync if we have a syncadapter, which means one is 1762 // outstanding 1763 if (currentSyncContext.mSyncAdapter != null) { 1764 try { 1765 currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext); 1766 } catch (RemoteException e) { 1767 // we don't need to retry this in this case 1768 } 1769 } 1770 1771 // pretend that the sync failed with an IOException, 1772 // which is a soft error 1773 SyncResult syncResult = new SyncResult(); 1774 syncResult.stats.numIoExceptions++; 1775 runSyncFinishedOrCanceledLocked(syncResult, currentSyncContext); 1776 1777 // since a sync just finished check if it is time to start a new sync 1778 nextPendingSyncTime = maybeStartNextSyncLocked(); 1779 } 1780 1781 break; 1782 } 1783 1784 case SyncHandler.MESSAGE_SYNC_ALARM: { 1785 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 1786 if (isLoggable) { 1787 Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_ALARM"); 1788 } 1789 mAlarmScheduleTime = null; 1790 try { 1791 nextPendingSyncTime = maybeStartNextSyncLocked(); 1792 } finally { 1793 mHandleAlarmWakeLock.release(); 1794 } 1795 break; 1796 } 1797 1798 case SyncHandler.MESSAGE_CHECK_ALARMS: 1799 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1800 Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_CHECK_ALARMS"); 1801 } 1802 nextPendingSyncTime = maybeStartNextSyncLocked(); 1803 break; 1804 } 1805 } finally { 1806 manageSyncNotificationLocked(); 1807 manageSyncAlarmLocked(earliestFuturePollTime, nextPendingSyncTime); 1808 mSyncTimeTracker.update(); 1809 mSyncManagerWakeLock.release(); 1810 } 1811 } 1812 1813 /** 1814 * Turn any periodic sync operations that are ready to run into pending sync operations. 1815 * @return the desired start time of the earliest future periodic sync operation, 1816 * in milliseconds since boot 1817 */ 1818 private long scheduleReadyPeriodicSyncs() { 1819 final boolean backgroundDataUsageAllowed = 1820 getConnectivityManager().getBackgroundDataSetting(); 1821 long earliestFuturePollTime = Long.MAX_VALUE; 1822 if (!backgroundDataUsageAllowed) { 1823 return earliestFuturePollTime; 1824 } 1825 1826 AccountAndUser[] accounts = mRunningAccounts; 1827 1828 final long nowAbsolute = System.currentTimeMillis(); 1829 final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis) 1830 ? (nowAbsolute - mSyncRandomOffsetMillis) : 0; 1831 1832 ArrayList<SyncStorageEngine.AuthorityInfo> infos = mSyncStorageEngine.getAuthorities(); 1833 for (SyncStorageEngine.AuthorityInfo info : infos) { 1834 // skip the sync if the account of this operation no longer exists 1835 if (!containsAccountAndUser(accounts, info.account, info.userId)) { 1836 continue; 1837 } 1838 1839 if (!mSyncStorageEngine.getMasterSyncAutomatically(info.userId) 1840 || !mSyncStorageEngine.getSyncAutomatically(info.account, info.userId, 1841 info.authority)) { 1842 continue; 1843 } 1844 1845 if (mSyncStorageEngine.getIsSyncable(info.account, info.userId, info.authority) 1846 == 0) { 1847 continue; 1848 } 1849 1850 SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(info); 1851 for (int i = 0, N = info.periodicSyncs.size(); i < N; i++) { 1852 final Bundle extras = info.periodicSyncs.get(i).first; 1853 final Long periodInMillis = info.periodicSyncs.get(i).second * 1000; 1854 // find when this periodic sync was last scheduled to run 1855 final long lastPollTimeAbsolute = status.getPeriodicSyncTime(i); 1856 1857 long remainingMillis 1858 = periodInMillis - (shiftedNowAbsolute % periodInMillis); 1859 1860 /* 1861 * Sync scheduling strategy: 1862 * Set the next periodic sync based on a random offset (in seconds). 1863 * 1864 * Also sync right now if any of the following cases hold 1865 * and mark it as having been scheduled 1866 * 1867 * Case 1: This sync is ready to run now. 1868 * Case 2: If the lastPollTimeAbsolute is in the future, 1869 * sync now and reinitialize. This can happen for 1870 * example if the user changed the time, synced and 1871 * changed back. 1872 * Case 3: If we failed to sync at the last scheduled time 1873 */ 1874 if (remainingMillis == periodInMillis // Case 1 1875 || lastPollTimeAbsolute > nowAbsolute // Case 2 1876 || (nowAbsolute - lastPollTimeAbsolute 1877 >= periodInMillis)) { // Case 3 1878 // Sync now 1879 final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff( 1880 info.account, info.userId, info.authority); 1881 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; 1882 syncAdapterInfo = mSyncAdapters.getServiceInfo( 1883 SyncAdapterType.newKey(info.authority, info.account.type), 1884 info.userId); 1885 if (syncAdapterInfo == null) { 1886 continue; 1887 } 1888 scheduleSyncOperation( 1889 new SyncOperation(info.account, info.userId, 1890 SyncStorageEngine.SOURCE_PERIODIC, 1891 info.authority, extras, 0 /* delay */, 1892 backoff != null ? backoff.first : 0, 1893 mSyncStorageEngine.getDelayUntilTime( 1894 info.account, info.userId, info.authority), 1895 syncAdapterInfo.type.allowParallelSyncs())); 1896 status.setPeriodicSyncTime(i, nowAbsolute); 1897 } 1898 // Compute when this periodic sync should next run 1899 final long nextPollTimeAbsolute = nowAbsolute + remainingMillis; 1900 1901 // remember this time if it is earlier than earliestFuturePollTime 1902 if (nextPollTimeAbsolute < earliestFuturePollTime) { 1903 earliestFuturePollTime = nextPollTimeAbsolute; 1904 } 1905 } 1906 } 1907 1908 if (earliestFuturePollTime == Long.MAX_VALUE) { 1909 return Long.MAX_VALUE; 1910 } 1911 1912 // convert absolute time to elapsed time 1913 return SystemClock.elapsedRealtime() 1914 + ((earliestFuturePollTime < nowAbsolute) 1915 ? 0 1916 : (earliestFuturePollTime - nowAbsolute)); 1917 } 1918 1919 private long maybeStartNextSyncLocked() { 1920 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 1921 if (isLoggable) Log.v(TAG, "maybeStartNextSync"); 1922 1923 // If we aren't ready to run (e.g. the data connection is down), get out. 1924 if (!mDataConnectionIsConnected) { 1925 if (isLoggable) { 1926 Log.v(TAG, "maybeStartNextSync: no data connection, skipping"); 1927 } 1928 return Long.MAX_VALUE; 1929 } 1930 1931 if (mStorageIsLow) { 1932 if (isLoggable) { 1933 Log.v(TAG, "maybeStartNextSync: memory low, skipping"); 1934 } 1935 return Long.MAX_VALUE; 1936 } 1937 1938 // If the accounts aren't known yet then we aren't ready to run. We will be kicked 1939 // when the account lookup request does complete. 1940 AccountAndUser[] accounts = mRunningAccounts; 1941 if (accounts == INITIAL_ACCOUNTS_ARRAY) { 1942 if (isLoggable) { 1943 Log.v(TAG, "maybeStartNextSync: accounts not known, skipping"); 1944 } 1945 return Long.MAX_VALUE; 1946 } 1947 1948 // Otherwise consume SyncOperations from the head of the SyncQueue until one is 1949 // found that is runnable (not disabled, etc). If that one is ready to run then 1950 // start it, otherwise just get out. 1951 final boolean backgroundDataUsageAllowed = 1952 getConnectivityManager().getBackgroundDataSetting(); 1953 1954 final long now = SystemClock.elapsedRealtime(); 1955 1956 // will be set to the next time that a sync should be considered for running 1957 long nextReadyToRunTime = Long.MAX_VALUE; 1958 1959 // order the sync queue, dropping syncs that are not allowed 1960 ArrayList<SyncOperation> operations = new ArrayList<SyncOperation>(); 1961 synchronized (mSyncQueue) { 1962 if (isLoggable) { 1963 Log.v(TAG, "build the operation array, syncQueue size is " 1964 + mSyncQueue.getOperations().size()); 1965 } 1966 final Iterator<SyncOperation> operationIterator = mSyncQueue.getOperations() 1967 .iterator(); 1968 1969 final ActivityManager activityManager 1970 = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); 1971 final Set<Integer> removedUsers = Sets.newHashSet(); 1972 while (operationIterator.hasNext()) { 1973 final SyncOperation op = operationIterator.next(); 1974 1975 // drop the sync if the account of this operation no longer exists 1976 if (!containsAccountAndUser(accounts, op.account, op.userId)) { 1977 operationIterator.remove(); 1978 mSyncStorageEngine.deleteFromPending(op.pendingOperation); 1979 continue; 1980 } 1981 1982 // drop this sync request if it isn't syncable 1983 int syncableState = mSyncStorageEngine.getIsSyncable( 1984 op.account, op.userId, op.authority); 1985 if (syncableState == 0) { 1986 operationIterator.remove(); 1987 mSyncStorageEngine.deleteFromPending(op.pendingOperation); 1988 continue; 1989 } 1990 1991 // if the user in not running, drop the request 1992 if (!activityManager.isUserRunning(op.userId)) { 1993 final UserInfo userInfo = mUserManager.getUserInfo(op.userId); 1994 if (userInfo == null) { 1995 removedUsers.add(op.userId); 1996 } 1997 continue; 1998 } 1999 2000 // if the next run time is in the future, meaning there are no syncs ready 2001 // to run, return the time 2002 if (op.effectiveRunTime > now) { 2003 if (nextReadyToRunTime > op.effectiveRunTime) { 2004 nextReadyToRunTime = op.effectiveRunTime; 2005 } 2006 continue; 2007 } 2008 2009 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; 2010 syncAdapterInfo = mSyncAdapters.getServiceInfo( 2011 SyncAdapterType.newKey(op.authority, op.account.type), op.userId); 2012 2013 // only proceed if network is connected for requesting UID 2014 final boolean uidNetworkConnected; 2015 if (syncAdapterInfo != null) { 2016 final NetworkInfo networkInfo = getConnectivityManager() 2017 .getActiveNetworkInfoForUid(syncAdapterInfo.uid); 2018 uidNetworkConnected = networkInfo != null && networkInfo.isConnected(); 2019 } else { 2020 uidNetworkConnected = false; 2021 } 2022 2023 // skip the sync if it isn't manual, and auto sync or 2024 // background data usage is disabled or network is 2025 // disconnected for the target UID. 2026 if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false) 2027 && (syncableState > 0) 2028 && (!mSyncStorageEngine.getMasterSyncAutomatically(op.userId) 2029 || !backgroundDataUsageAllowed 2030 || !uidNetworkConnected 2031 || !mSyncStorageEngine.getSyncAutomatically( 2032 op.account, op.userId, op.authority))) { 2033 operationIterator.remove(); 2034 mSyncStorageEngine.deleteFromPending(op.pendingOperation); 2035 continue; 2036 } 2037 2038 operations.add(op); 2039 } 2040 for (Integer user : removedUsers) { 2041 // if it's still removed 2042 if (mUserManager.getUserInfo(user) == null) { 2043 onUserRemoved(user); 2044 } 2045 } 2046 } 2047 2048 // find the next operation to dispatch, if one is ready 2049 // iterate from the top, keep issuing (while potentially cancelling existing syncs) 2050 // until the quotas are filled. 2051 // once the quotas are filled iterate once more to find when the next one would be 2052 // (also considering pre-emption reasons). 2053 if (isLoggable) Log.v(TAG, "sort the candidate operations, size " + operations.size()); 2054 Collections.sort(operations); 2055 if (isLoggable) Log.v(TAG, "dispatch all ready sync operations"); 2056 for (int i = 0, N = operations.size(); i < N; i++) { 2057 final SyncOperation candidate = operations.get(i); 2058 final boolean candidateIsInitialization = candidate.isInitialization(); 2059 2060 int numInit = 0; 2061 int numRegular = 0; 2062 ActiveSyncContext conflict = null; 2063 ActiveSyncContext longRunning = null; 2064 ActiveSyncContext toReschedule = null; 2065 ActiveSyncContext oldestNonExpeditedRegular = null; 2066 2067 for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) { 2068 final SyncOperation activeOp = activeSyncContext.mSyncOperation; 2069 if (activeOp.isInitialization()) { 2070 numInit++; 2071 } else { 2072 numRegular++; 2073 if (!activeOp.isExpedited()) { 2074 if (oldestNonExpeditedRegular == null 2075 || (oldestNonExpeditedRegular.mStartTime 2076 > activeSyncContext.mStartTime)) { 2077 oldestNonExpeditedRegular = activeSyncContext; 2078 } 2079 } 2080 } 2081 if (activeOp.account.type.equals(candidate.account.type) 2082 && activeOp.authority.equals(candidate.authority) 2083 && activeOp.userId == candidate.userId 2084 && (!activeOp.allowParallelSyncs 2085 || activeOp.account.name.equals(candidate.account.name))) { 2086 conflict = activeSyncContext; 2087 // don't break out since we want to do a full count of the varieties 2088 } else { 2089 if (candidateIsInitialization == activeOp.isInitialization() 2090 && activeSyncContext.mStartTime + MAX_TIME_PER_SYNC < now) { 2091 longRunning = activeSyncContext; 2092 // don't break out since we want to do a full count of the varieties 2093 } 2094 } 2095 } 2096 2097 if (isLoggable) { 2098 Log.v(TAG, "candidate " + (i + 1) + " of " + N + ": " + candidate); 2099 Log.v(TAG, " numActiveInit=" + numInit + ", numActiveRegular=" + numRegular); 2100 Log.v(TAG, " longRunning: " + longRunning); 2101 Log.v(TAG, " conflict: " + conflict); 2102 Log.v(TAG, " oldestNonExpeditedRegular: " + oldestNonExpeditedRegular); 2103 } 2104 2105 final boolean roomAvailable = candidateIsInitialization 2106 ? numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS 2107 : numRegular < MAX_SIMULTANEOUS_REGULAR_SYNCS; 2108 2109 if (conflict != null) { 2110 if (candidateIsInitialization && !conflict.mSyncOperation.isInitialization() 2111 && numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS) { 2112 toReschedule = conflict; 2113 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2114 Log.v(TAG, "canceling and rescheduling sync since an initialization " 2115 + "takes higher priority, " + conflict); 2116 } 2117 } else if (candidate.expedited && !conflict.mSyncOperation.expedited 2118 && (candidateIsInitialization 2119 == conflict.mSyncOperation.isInitialization())) { 2120 toReschedule = conflict; 2121 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2122 Log.v(TAG, "canceling and rescheduling sync since an expedited " 2123 + "takes higher priority, " + conflict); 2124 } 2125 } else { 2126 continue; 2127 } 2128 } else if (roomAvailable) { 2129 // dispatch candidate 2130 } else if (candidate.isExpedited() && oldestNonExpeditedRegular != null 2131 && !candidateIsInitialization) { 2132 // We found an active, non-expedited regular sync. We also know that the 2133 // candidate doesn't conflict with this active sync since conflict 2134 // is null. Reschedule the active sync and start the candidate. 2135 toReschedule = oldestNonExpeditedRegular; 2136 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2137 Log.v(TAG, "canceling and rescheduling sync since an expedited is ready to run, " 2138 + oldestNonExpeditedRegular); 2139 } 2140 } else if (longRunning != null 2141 && (candidateIsInitialization 2142 == longRunning.mSyncOperation.isInitialization())) { 2143 // We found an active, long-running sync. Reschedule the active 2144 // sync and start the candidate. 2145 toReschedule = longRunning; 2146 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2147 Log.v(TAG, "canceling and rescheduling sync since it ran roo long, " 2148 + longRunning); 2149 } 2150 } else { 2151 // we were unable to find or make space to run this candidate, go on to 2152 // the next one 2153 continue; 2154 } 2155 2156 if (toReschedule != null) { 2157 runSyncFinishedOrCanceledLocked(null, toReschedule); 2158 scheduleSyncOperation(toReschedule.mSyncOperation); 2159 } 2160 synchronized (mSyncQueue) { 2161 mSyncQueue.remove(candidate); 2162 } 2163 dispatchSyncOperation(candidate); 2164 } 2165 2166 return nextReadyToRunTime; 2167 } 2168 2169 private boolean dispatchSyncOperation(SyncOperation op) { 2170 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2171 Log.v(TAG, "dispatchSyncOperation: we are going to sync " + op); 2172 Log.v(TAG, "num active syncs: " + mActiveSyncContexts.size()); 2173 for (ActiveSyncContext syncContext : mActiveSyncContexts) { 2174 Log.v(TAG, syncContext.toString()); 2175 } 2176 } 2177 2178 // connect to the sync adapter 2179 SyncAdapterType syncAdapterType = SyncAdapterType.newKey(op.authority, op.account.type); 2180 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; 2181 syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, op.userId); 2182 if (syncAdapterInfo == null) { 2183 Log.d(TAG, "can't find a sync adapter for " + syncAdapterType 2184 + ", removing settings for it"); 2185 mSyncStorageEngine.removeAuthority(op.account, op.userId, op.authority); 2186 return false; 2187 } 2188 2189 ActiveSyncContext activeSyncContext = 2190 new ActiveSyncContext(op, insertStartSyncEvent(op), syncAdapterInfo.uid); 2191 activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext); 2192 mActiveSyncContexts.add(activeSyncContext); 2193 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2194 Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext); 2195 } 2196 if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo, op.userId)) { 2197 Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo); 2198 closeActiveSyncContext(activeSyncContext); 2199 return false; 2200 } 2201 2202 return true; 2203 } 2204 2205 private void runBoundToSyncAdapter(final ActiveSyncContext activeSyncContext, 2206 ISyncAdapter syncAdapter) { 2207 activeSyncContext.mSyncAdapter = syncAdapter; 2208 final SyncOperation syncOperation = activeSyncContext.mSyncOperation; 2209 try { 2210 activeSyncContext.mIsLinkedToDeath = true; 2211 syncAdapter.asBinder().linkToDeath(activeSyncContext, 0); 2212 2213 syncAdapter.startSync(activeSyncContext, syncOperation.authority, 2214 syncOperation.account, syncOperation.extras); 2215 } catch (RemoteException remoteExc) { 2216 Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc); 2217 closeActiveSyncContext(activeSyncContext); 2218 increaseBackoffSetting(syncOperation); 2219 scheduleSyncOperation(new SyncOperation(syncOperation)); 2220 } catch (RuntimeException exc) { 2221 closeActiveSyncContext(activeSyncContext); 2222 Log.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc); 2223 } 2224 } 2225 2226 private void cancelActiveSyncLocked(Account account, int userId, String authority) { 2227 ArrayList<ActiveSyncContext> activeSyncs = 2228 new ArrayList<ActiveSyncContext>(mActiveSyncContexts); 2229 for (ActiveSyncContext activeSyncContext : activeSyncs) { 2230 if (activeSyncContext != null) { 2231 // if an account was specified then only cancel the sync if it matches 2232 if (account != null) { 2233 if (!account.equals(activeSyncContext.mSyncOperation.account)) { 2234 continue; 2235 } 2236 } 2237 // if an authority was specified then only cancel the sync if it matches 2238 if (authority != null) { 2239 if (!authority.equals(activeSyncContext.mSyncOperation.authority)) { 2240 continue; 2241 } 2242 } 2243 // check if the userid matches 2244 if (userId != UserHandle.USER_ALL 2245 && userId != activeSyncContext.mSyncOperation.userId) { 2246 continue; 2247 } 2248 runSyncFinishedOrCanceledLocked(null /* no result since this is a cancel */, 2249 activeSyncContext); 2250 } 2251 } 2252 } 2253 2254 private void runSyncFinishedOrCanceledLocked(SyncResult syncResult, 2255 ActiveSyncContext activeSyncContext) { 2256 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2257 2258 if (activeSyncContext.mIsLinkedToDeath) { 2259 activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0); 2260 activeSyncContext.mIsLinkedToDeath = false; 2261 } 2262 closeActiveSyncContext(activeSyncContext); 2263 2264 final SyncOperation syncOperation = activeSyncContext.mSyncOperation; 2265 2266 final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime; 2267 2268 String historyMessage; 2269 int downstreamActivity; 2270 int upstreamActivity; 2271 if (syncResult != null) { 2272 if (isLoggable) { 2273 Log.v(TAG, "runSyncFinishedOrCanceled [finished]: " 2274 + syncOperation + ", result " + syncResult); 2275 } 2276 2277 if (!syncResult.hasError()) { 2278 historyMessage = SyncStorageEngine.MESG_SUCCESS; 2279 // TODO: set these correctly when the SyncResult is extended to include it 2280 downstreamActivity = 0; 2281 upstreamActivity = 0; 2282 clearBackoffSetting(syncOperation); 2283 } else { 2284 Log.d(TAG, "failed sync operation " + syncOperation + ", " + syncResult); 2285 // the operation failed so increase the backoff time 2286 if (!syncResult.syncAlreadyInProgress) { 2287 increaseBackoffSetting(syncOperation); 2288 } 2289 // reschedule the sync if so indicated by the syncResult 2290 maybeRescheduleSync(syncResult, syncOperation); 2291 historyMessage = Integer.toString(syncResultToErrorNumber(syncResult)); 2292 // TODO: set these correctly when the SyncResult is extended to include it 2293 downstreamActivity = 0; 2294 upstreamActivity = 0; 2295 } 2296 2297 setDelayUntilTime(syncOperation, syncResult.delayUntil); 2298 } else { 2299 if (isLoggable) { 2300 Log.v(TAG, "runSyncFinishedOrCanceled [canceled]: " + syncOperation); 2301 } 2302 if (activeSyncContext.mSyncAdapter != null) { 2303 try { 2304 activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext); 2305 } catch (RemoteException e) { 2306 // we don't need to retry this in this case 2307 } 2308 } 2309 historyMessage = SyncStorageEngine.MESG_CANCELED; 2310 downstreamActivity = 0; 2311 upstreamActivity = 0; 2312 } 2313 2314 stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage, 2315 upstreamActivity, downstreamActivity, elapsedTime); 2316 2317 if (syncResult != null && syncResult.tooManyDeletions) { 2318 installHandleTooManyDeletesNotification(syncOperation.account, 2319 syncOperation.authority, syncResult.stats.numDeletes, 2320 syncOperation.userId); 2321 } else { 2322 mNotificationMgr.cancelAsUser(null, 2323 syncOperation.account.hashCode() ^ syncOperation.authority.hashCode(), 2324 new UserHandle(syncOperation.userId)); 2325 } 2326 2327 if (syncResult != null && syncResult.fullSyncRequested) { 2328 scheduleSyncOperation(new SyncOperation(syncOperation.account, syncOperation.userId, 2329 syncOperation.syncSource, syncOperation.authority, new Bundle(), 0, 2330 syncOperation.backoff, syncOperation.delayUntil, 2331 syncOperation.allowParallelSyncs)); 2332 } 2333 // no need to schedule an alarm, as that will be done by our caller. 2334 } 2335 2336 private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) { 2337 activeSyncContext.close(); 2338 mActiveSyncContexts.remove(activeSyncContext); 2339 mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo, 2340 activeSyncContext.mSyncOperation.userId); 2341 } 2342 2343 /** 2344 * Convert the error-containing SyncResult into the Sync.History error number. Since 2345 * the SyncResult may indicate multiple errors at once, this method just returns the 2346 * most "serious" error. 2347 * @param syncResult the SyncResult from which to read 2348 * @return the most "serious" error set in the SyncResult 2349 * @throws IllegalStateException if the SyncResult does not indicate any errors. 2350 * If SyncResult.error() is true then it is safe to call this. 2351 */ 2352 private int syncResultToErrorNumber(SyncResult syncResult) { 2353 if (syncResult.syncAlreadyInProgress) 2354 return ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS; 2355 if (syncResult.stats.numAuthExceptions > 0) 2356 return ContentResolver.SYNC_ERROR_AUTHENTICATION; 2357 if (syncResult.stats.numIoExceptions > 0) 2358 return ContentResolver.SYNC_ERROR_IO; 2359 if (syncResult.stats.numParseExceptions > 0) 2360 return ContentResolver.SYNC_ERROR_PARSE; 2361 if (syncResult.stats.numConflictDetectedExceptions > 0) 2362 return ContentResolver.SYNC_ERROR_CONFLICT; 2363 if (syncResult.tooManyDeletions) 2364 return ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS; 2365 if (syncResult.tooManyRetries) 2366 return ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES; 2367 if (syncResult.databaseError) 2368 return ContentResolver.SYNC_ERROR_INTERNAL; 2369 throw new IllegalStateException("we are not in an error state, " + syncResult); 2370 } 2371 2372 private void manageSyncNotificationLocked() { 2373 boolean shouldCancel; 2374 boolean shouldInstall; 2375 2376 if (mActiveSyncContexts.isEmpty()) { 2377 mSyncNotificationInfo.startTime = null; 2378 2379 // we aren't syncing. if the notification is active then remember that we need 2380 // to cancel it and then clear out the info 2381 shouldCancel = mSyncNotificationInfo.isActive; 2382 shouldInstall = false; 2383 } else { 2384 // we are syncing 2385 final long now = SystemClock.elapsedRealtime(); 2386 if (mSyncNotificationInfo.startTime == null) { 2387 mSyncNotificationInfo.startTime = now; 2388 } 2389 2390 // there are three cases: 2391 // - the notification is up: do nothing 2392 // - the notification is not up but it isn't time yet: don't install 2393 // - the notification is not up and it is time: need to install 2394 2395 if (mSyncNotificationInfo.isActive) { 2396 shouldInstall = shouldCancel = false; 2397 } else { 2398 // it isn't currently up, so there is nothing to cancel 2399 shouldCancel = false; 2400 2401 final boolean timeToShowNotification = 2402 now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY; 2403 if (timeToShowNotification) { 2404 shouldInstall = true; 2405 } else { 2406 // show the notification immediately if this is a manual sync 2407 shouldInstall = false; 2408 for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) { 2409 final boolean manualSync = activeSyncContext.mSyncOperation.extras 2410 .getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false); 2411 if (manualSync) { 2412 shouldInstall = true; 2413 break; 2414 } 2415 } 2416 } 2417 } 2418 } 2419 2420 if (shouldCancel && !shouldInstall) { 2421 mNeedSyncActiveNotification = false; 2422 sendSyncStateIntent(); 2423 mSyncNotificationInfo.isActive = false; 2424 } 2425 2426 if (shouldInstall) { 2427 mNeedSyncActiveNotification = true; 2428 sendSyncStateIntent(); 2429 mSyncNotificationInfo.isActive = true; 2430 } 2431 } 2432 2433 private void manageSyncAlarmLocked(long nextPeriodicEventElapsedTime, 2434 long nextPendingEventElapsedTime) { 2435 // in each of these cases the sync loop will be kicked, which will cause this 2436 // method to be called again 2437 if (!mDataConnectionIsConnected) return; 2438 if (mStorageIsLow) return; 2439 2440 // When the status bar notification should be raised 2441 final long notificationTime = 2442 (!mSyncHandler.mSyncNotificationInfo.isActive 2443 && mSyncHandler.mSyncNotificationInfo.startTime != null) 2444 ? mSyncHandler.mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY 2445 : Long.MAX_VALUE; 2446 2447 // When we should consider canceling an active sync 2448 long earliestTimeoutTime = Long.MAX_VALUE; 2449 for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) { 2450 final long currentSyncTimeoutTime = 2451 currentSyncContext.mTimeoutStartTime + MAX_TIME_PER_SYNC; 2452 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2453 Log.v(TAG, "manageSyncAlarm: active sync, mTimeoutStartTime + MAX is " 2454 + currentSyncTimeoutTime); 2455 } 2456 if (earliestTimeoutTime > currentSyncTimeoutTime) { 2457 earliestTimeoutTime = currentSyncTimeoutTime; 2458 } 2459 } 2460 2461 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2462 Log.v(TAG, "manageSyncAlarm: notificationTime is " + notificationTime); 2463 } 2464 2465 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2466 Log.v(TAG, "manageSyncAlarm: earliestTimeoutTime is " + earliestTimeoutTime); 2467 } 2468 2469 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2470 Log.v(TAG, "manageSyncAlarm: nextPeriodicEventElapsedTime is " 2471 + nextPeriodicEventElapsedTime); 2472 } 2473 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2474 Log.v(TAG, "manageSyncAlarm: nextPendingEventElapsedTime is " 2475 + nextPendingEventElapsedTime); 2476 } 2477 2478 long alarmTime = Math.min(notificationTime, earliestTimeoutTime); 2479 alarmTime = Math.min(alarmTime, nextPeriodicEventElapsedTime); 2480 alarmTime = Math.min(alarmTime, nextPendingEventElapsedTime); 2481 2482 // Bound the alarm time. 2483 final long now = SystemClock.elapsedRealtime(); 2484 if (alarmTime < now + SYNC_ALARM_TIMEOUT_MIN) { 2485 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2486 Log.v(TAG, "manageSyncAlarm: the alarmTime is too small, " 2487 + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN)); 2488 } 2489 alarmTime = now + SYNC_ALARM_TIMEOUT_MIN; 2490 } else if (alarmTime > now + SYNC_ALARM_TIMEOUT_MAX) { 2491 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2492 Log.v(TAG, "manageSyncAlarm: the alarmTime is too large, " 2493 + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN)); 2494 } 2495 alarmTime = now + SYNC_ALARM_TIMEOUT_MAX; 2496 } 2497 2498 // determine if we need to set or cancel the alarm 2499 boolean shouldSet = false; 2500 boolean shouldCancel = false; 2501 final boolean alarmIsActive = mAlarmScheduleTime != null; 2502 final boolean needAlarm = alarmTime != Long.MAX_VALUE; 2503 if (needAlarm) { 2504 if (!alarmIsActive || alarmTime < mAlarmScheduleTime) { 2505 shouldSet = true; 2506 } 2507 } else { 2508 shouldCancel = alarmIsActive; 2509 } 2510 2511 // set or cancel the alarm as directed 2512 ensureAlarmService(); 2513 if (shouldSet) { 2514 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2515 Log.v(TAG, "requesting that the alarm manager wake us up at elapsed time " 2516 + alarmTime + ", now is " + now + ", " + ((alarmTime - now) / 1000) 2517 + " secs from now"); 2518 } 2519 mAlarmScheduleTime = alarmTime; 2520 mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime, 2521 mSyncAlarmIntent); 2522 } else if (shouldCancel) { 2523 mAlarmScheduleTime = null; 2524 mAlarmService.cancel(mSyncAlarmIntent); 2525 } 2526 } 2527 2528 private void sendSyncStateIntent() { 2529 Intent syncStateIntent = new Intent(Intent.ACTION_SYNC_STATE_CHANGED); 2530 syncStateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 2531 syncStateIntent.putExtra("active", mNeedSyncActiveNotification); 2532 syncStateIntent.putExtra("failing", false); 2533 mContext.sendBroadcastAsUser(syncStateIntent, UserHandle.OWNER); 2534 } 2535 2536 private void installHandleTooManyDeletesNotification(Account account, String authority, 2537 long numDeletes, int userId) { 2538 if (mNotificationMgr == null) return; 2539 2540 final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider( 2541 authority, 0 /* flags */); 2542 if (providerInfo == null) { 2543 return; 2544 } 2545 CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager()); 2546 2547 Intent clickIntent = new Intent(mContext, SyncActivityTooManyDeletes.class); 2548 clickIntent.putExtra("account", account); 2549 clickIntent.putExtra("authority", authority); 2550 clickIntent.putExtra("provider", authorityName.toString()); 2551 clickIntent.putExtra("numDeletes", numDeletes); 2552 2553 if (!isActivityAvailable(clickIntent)) { 2554 Log.w(TAG, "No activity found to handle too many deletes."); 2555 return; 2556 } 2557 2558 final PendingIntent pendingIntent = PendingIntent 2559 .getActivityAsUser(mContext, 0, clickIntent, 2560 PendingIntent.FLAG_CANCEL_CURRENT, null, new UserHandle(userId)); 2561 2562 CharSequence tooManyDeletesDescFormat = mContext.getResources().getText( 2563 R.string.contentServiceTooManyDeletesNotificationDesc); 2564 2565 Notification notification = 2566 new Notification(R.drawable.stat_notify_sync_error, 2567 mContext.getString(R.string.contentServiceSync), 2568 System.currentTimeMillis()); 2569 notification.setLatestEventInfo(mContext, 2570 mContext.getString(R.string.contentServiceSyncNotificationTitle), 2571 String.format(tooManyDeletesDescFormat.toString(), authorityName), 2572 pendingIntent); 2573 notification.flags |= Notification.FLAG_ONGOING_EVENT; 2574 mNotificationMgr.notifyAsUser(null, account.hashCode() ^ authority.hashCode(), 2575 notification, new UserHandle(userId)); 2576 } 2577 2578 /** 2579 * Checks whether an activity exists on the system image for the given intent. 2580 * 2581 * @param intent The intent for an activity. 2582 * @return Whether or not an activity exists. 2583 */ 2584 private boolean isActivityAvailable(Intent intent) { 2585 PackageManager pm = mContext.getPackageManager(); 2586 List<ResolveInfo> list = pm.queryIntentActivities(intent, 0); 2587 int listSize = list.size(); 2588 for (int i = 0; i < listSize; i++) { 2589 ResolveInfo resolveInfo = list.get(i); 2590 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 2591 != 0) { 2592 return true; 2593 } 2594 } 2595 2596 return false; 2597 } 2598 2599 public long insertStartSyncEvent(SyncOperation syncOperation) { 2600 final int source = syncOperation.syncSource; 2601 final long now = System.currentTimeMillis(); 2602 2603 EventLog.writeEvent(2720, syncOperation.authority, 2604 SyncStorageEngine.EVENT_START, source, 2605 syncOperation.account.name.hashCode()); 2606 2607 return mSyncStorageEngine.insertStartSyncEvent( 2608 syncOperation.account, syncOperation.userId, syncOperation.authority, 2609 now, source, syncOperation.isInitialization()); 2610 } 2611 2612 public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, 2613 int upstreamActivity, int downstreamActivity, long elapsedTime) { 2614 EventLog.writeEvent(2720, syncOperation.authority, 2615 SyncStorageEngine.EVENT_STOP, syncOperation.syncSource, 2616 syncOperation.account.name.hashCode()); 2617 2618 mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime, 2619 resultMessage, downstreamActivity, upstreamActivity); 2620 } 2621 } 2622 2623 private boolean isSyncStillActive(ActiveSyncContext activeSyncContext) { 2624 for (ActiveSyncContext sync : mActiveSyncContexts) { 2625 if (sync == activeSyncContext) { 2626 return true; 2627 } 2628 } 2629 return false; 2630 } 2631} 2632