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