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