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