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