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