SyncManager.java revision 8b1db0746fb5831cf670d4935acf1197b0c92748
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.content; 18 19import android.accounts.Account; 20import android.accounts.AccountAndUser; 21import android.accounts.AccountManager; 22import android.app.ActivityManager; 23import android.app.AlarmManager; 24import android.app.AppGlobals; 25import android.app.Notification; 26import android.app.NotificationManager; 27import android.app.PendingIntent; 28import android.content.BroadcastReceiver; 29import android.content.ComponentName; 30import android.content.ContentResolver; 31import android.content.Context; 32import android.content.ISyncAdapter; 33import android.content.ISyncContext; 34import android.content.ISyncServiceAdapter; 35import android.content.ISyncStatusObserver; 36import android.content.Intent; 37import android.content.IntentFilter; 38import android.content.PeriodicSync; 39import android.content.ServiceConnection; 40import android.content.SyncActivityTooManyDeletes; 41import android.content.SyncAdapterType; 42import android.content.SyncAdaptersCache; 43import android.content.SyncInfo; 44import android.content.SyncResult; 45import android.content.SyncStatusInfo; 46import android.content.pm.ApplicationInfo; 47import android.content.pm.PackageInfo; 48import android.content.pm.PackageManager; 49import android.content.pm.PackageManager.NameNotFoundException; 50import android.content.pm.ProviderInfo; 51import android.content.pm.RegisteredServicesCache; 52import android.content.pm.RegisteredServicesCacheListener; 53import android.content.pm.ResolveInfo; 54import android.content.pm.UserInfo; 55import android.database.ContentObserver; 56import android.net.ConnectivityManager; 57import android.net.NetworkInfo; 58import android.net.TrafficStats; 59import android.os.BatteryStats; 60import android.os.Bundle; 61import android.os.Handler; 62import android.os.IBinder; 63import android.os.Looper; 64import android.os.Message; 65import android.os.PowerManager; 66import android.os.RemoteException; 67import android.os.ServiceManager; 68import android.os.SystemClock; 69import android.os.SystemProperties; 70import android.os.UserHandle; 71import android.os.UserManager; 72import android.os.WorkSource; 73import android.provider.Settings; 74import android.text.format.DateUtils; 75import android.text.format.Time; 76import android.text.TextUtils; 77import android.util.EventLog; 78import android.util.Log; 79import android.util.Pair; 80 81import com.android.internal.R; 82import com.android.internal.annotations.GuardedBy; 83import com.android.internal.app.IBatteryStats; 84import com.android.internal.os.BackgroundThread; 85import com.android.internal.util.IndentingPrintWriter; 86import com.android.server.accounts.AccountManagerService; 87import com.android.server.content.SyncStorageEngine.AuthorityInfo; 88import com.android.server.content.SyncStorageEngine.EndPoint; 89import com.android.server.content.SyncStorageEngine.OnSyncRequestListener; 90import com.google.android.collect.Lists; 91import com.google.android.collect.Maps; 92import com.google.android.collect.Sets; 93 94import java.io.FileDescriptor; 95import java.io.PrintWriter; 96import java.util.ArrayList; 97import java.util.Arrays; 98import java.util.Collection; 99import java.util.Collections; 100import java.util.Comparator; 101import java.util.HashMap; 102import java.util.HashSet; 103import java.util.Iterator; 104import java.util.List; 105import java.util.Map; 106import java.util.Random; 107import java.util.Set; 108 109/** 110 * @hide 111 */ 112public class SyncManager { 113 static final String TAG = "SyncManager"; 114 115 /** Delay a sync due to local changes this long. In milliseconds */ 116 private static final long LOCAL_SYNC_DELAY; 117 118 /** 119 * If a sync takes longer than this and the sync queue is not empty then we will 120 * cancel it and add it back to the end of the sync queue. In milliseconds. 121 */ 122 private static final long MAX_TIME_PER_SYNC; 123 124 static { 125 final boolean isLargeRAM = !ActivityManager.isLowRamDeviceStatic(); 126 int defaultMaxInitSyncs = isLargeRAM ? 5 : 2; 127 int defaultMaxRegularSyncs = isLargeRAM ? 2 : 1; 128 MAX_SIMULTANEOUS_INITIALIZATION_SYNCS = 129 SystemProperties.getInt("sync.max_init_syncs", defaultMaxInitSyncs); 130 MAX_SIMULTANEOUS_REGULAR_SYNCS = 131 SystemProperties.getInt("sync.max_regular_syncs", defaultMaxRegularSyncs); 132 LOCAL_SYNC_DELAY = 133 SystemProperties.getLong("sync.local_sync_delay", 30 * 1000 /* 30 seconds */); 134 MAX_TIME_PER_SYNC = 135 SystemProperties.getLong("sync.max_time_per_sync", 5 * 60 * 1000 /* 5 minutes */); 136 SYNC_NOTIFICATION_DELAY = 137 SystemProperties.getLong("sync.notification_delay", 30 * 1000 /* 30 seconds */); 138 } 139 140 private static final long SYNC_NOTIFICATION_DELAY; 141 142 /** 143 * When retrying a sync for the first time use this delay. After that 144 * the retry time will double until it reached MAX_SYNC_RETRY_TIME. 145 * In milliseconds. 146 */ 147 private static final long INITIAL_SYNC_RETRY_TIME_IN_MS = 30 * 1000; // 30 seconds 148 149 /** 150 * Default the max sync retry time to this value. 151 */ 152 private static final long DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS = 60 * 60; // one hour 153 154 /** 155 * How long to wait before retrying a sync that failed due to one already being in progress. 156 */ 157 private static final int DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS = 10; 158 159 /** 160 * How long to wait before considering an active sync to have timed-out, and cancelling it. 161 */ 162 private static final long ACTIVE_SYNC_TIMEOUT_MILLIS = 30L * 60 * 1000; // 30 mins 163 164 /** 165 * How often to periodically poll network traffic for an adapter performing a sync to determine 166 * whether progress is being made. 167 */ 168 private static final long SYNC_MONITOR_WINDOW_LENGTH_MILLIS = 60 * 1000; // 60 seconds 169 170 /** 171 * How many bytes must be transferred (Tx + Rx) over the period of time defined by 172 * {@link #SYNC_MONITOR_WINDOW_LENGTH_MILLIS} for the sync to be considered to be making 173 * progress. 174 */ 175 private static final int SYNC_MONITOR_PROGRESS_THRESHOLD_BYTES = 10; // 10 bytes 176 177 /** 178 * How long to delay each queued {@link SyncHandler} message that may have occurred before boot 179 * or befor the device became provisioned. 180 */ 181 private static final long PER_SYNC_BOOT_DELAY_MILLIS = 3000L; // 3 seconds 182 183 /** 184 * The maximum amount of time we're willing to delay syncs out of boot, after device has been 185 * provisioned, etc. 186 */ 187 private static final long MAX_SYNC_BOOT_DELAY_MILLIS = 120000L; // 2 minutes 188 189 private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*/"; 190 private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm"; 191 private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock"; 192 193 private static final int MAX_SIMULTANEOUS_REGULAR_SYNCS; 194 private static final int MAX_SIMULTANEOUS_INITIALIZATION_SYNCS; 195 196 private Context mContext; 197 198 private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0]; 199 200 // TODO: add better locking around mRunningAccounts 201 private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY; 202 203 volatile private PowerManager.WakeLock mHandleAlarmWakeLock; 204 volatile private PowerManager.WakeLock mSyncManagerWakeLock; 205 volatile private boolean mDataConnectionIsConnected = false; 206 volatile private boolean mStorageIsLow = false; 207 volatile private boolean mDeviceIsIdle = false; 208 209 private final NotificationManager mNotificationMgr; 210 private AlarmManager mAlarmService = null; 211 private final IBatteryStats mBatteryStats; 212 213 private SyncStorageEngine mSyncStorageEngine; 214 215 @GuardedBy("mSyncQueue") 216 private final SyncQueue mSyncQueue; 217 218 protected final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList(); 219 220 // set if the sync active indicator should be reported 221 private boolean mNeedSyncActiveNotification = false; 222 223 private final PendingIntent mSyncAlarmIntent; 224 // Synchronized on "this". Instead of using this directly one should instead call 225 // its accessor, getConnManager(). 226 private ConnectivityManager mConnManagerDoNotUseDirectly; 227 228 /** Track whether the device has already been provisioned. */ 229 private boolean mProvisioned; 230 231 protected SyncAdaptersCache mSyncAdapters; 232 233 private final AppIdleMonitor mAppIdleMonitor; 234 235 private BroadcastReceiver mStorageIntentReceiver = 236 new BroadcastReceiver() { 237 @Override 238 public void onReceive(Context context, Intent intent) { 239 String action = intent.getAction(); 240 if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) { 241 if (Log.isLoggable(TAG, Log.VERBOSE)) { 242 Log.v(TAG, "Internal storage is low."); 243 } 244 mStorageIsLow = true; 245 cancelActiveSync( 246 SyncStorageEngine.EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL, 247 null /* any sync */); 248 } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { 249 if (Log.isLoggable(TAG, Log.VERBOSE)) { 250 Log.v(TAG, "Internal storage is ok."); 251 } 252 mStorageIsLow = false; 253 sendCheckAlarmsMessage(); 254 } 255 } 256 }; 257 258 private BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() { 259 @Override public void onReceive(Context context, Intent intent) { 260 boolean idle = mPowerManager.isDeviceIdleMode(); 261 mDeviceIsIdle = idle; 262 if (idle) { 263 cancelActiveSync( 264 SyncStorageEngine.EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL, 265 null /* any sync */); 266 } else { 267 sendCheckAlarmsMessage(); 268 } 269 } 270 }; 271 272 private BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { 273 @Override 274 public void onReceive(Context context, Intent intent) { 275 mBootCompleted = true; 276 mSyncHandler.onBootCompleted(); 277 } 278 }; 279 280 private BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() { 281 @Override 282 public void onReceive(Context context, Intent intent) { 283 updateRunningAccounts(); 284 285 // Kick off sync for everyone, since this was a radical account change 286 scheduleSync(null, UserHandle.USER_ALL, SyncOperation.REASON_ACCOUNTS_UPDATED, null, 287 null, 0 /* no delay */, 0/* no delay */, false); 288 } 289 }; 290 291 private final PowerManager mPowerManager; 292 293 // Use this as a random offset to seed all periodic syncs. 294 private int mSyncRandomOffsetMillis; 295 296 private final UserManager mUserManager; 297 298 private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds 299 private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours 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, mContext); 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(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.print("notification info: "); 1500 final StringBuilder sb = new StringBuilder(); 1501 mSyncHandler.mSyncNotificationInfo.toString(sb); 1502 pw.println(sb.toString()); 1503 1504 pw.println(); 1505 pw.println("Active Syncs: " + mActiveSyncContexts.size()); 1506 final PackageManager pm = mContext.getPackageManager(); 1507 for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) { 1508 final long durationInSeconds = (now - activeSyncContext.mStartTime) / 1000; 1509 pw.print(" "); 1510 pw.print(DateUtils.formatElapsedTime(durationInSeconds)); 1511 pw.print(" - "); 1512 pw.print(activeSyncContext.mSyncOperation.dump(pm, false)); 1513 pw.println(); 1514 } 1515 1516 synchronized (mSyncQueue) { 1517 sb.setLength(0); 1518 mSyncQueue.dump(sb); 1519 // Dump Pending Operations. 1520 getSyncStorageEngine().dumpPendingOperations(sb); 1521 } 1522 1523 pw.println(); 1524 pw.print(sb.toString()); 1525 1526 // join the installed sync adapter with the accounts list and emit for everything 1527 pw.println(); 1528 pw.println("Sync Status"); 1529 for (AccountAndUser account : accounts) { 1530 pw.printf("Account %s u%d %s\n", 1531 account.account.name, account.userId, account.account.type); 1532 1533 pw.println("======================================================================="); 1534 final PrintTable table = new PrintTable(13); 1535 table.set(0, 0, 1536 "Authority", // 0 1537 "Syncable", // 1 1538 "Enabled", // 2 1539 "Delay", // 3 1540 "Loc", // 4 1541 "Poll", // 5 1542 "Per", // 6 1543 "Serv", // 7 1544 "User", // 8 1545 "Tot", // 9 1546 "Time", // 10 1547 "Last Sync", // 11 1548 "Periodic" // 12 1549 ); 1550 1551 final List<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> sorted = 1552 Lists.newArrayList(); 1553 sorted.addAll(mSyncAdapters.getAllServices(account.userId)); 1554 Collections.sort(sorted, 1555 new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() { 1556 @Override 1557 public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs, 1558 RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) { 1559 return lhs.type.authority.compareTo(rhs.type.authority); 1560 } 1561 }); 1562 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : sorted) { 1563 if (!syncAdapterType.type.accountType.equals(account.account.type)) { 1564 continue; 1565 } 1566 int row = table.getNumRows(); 1567 Pair<AuthorityInfo, SyncStatusInfo> syncAuthoritySyncStatus = 1568 mSyncStorageEngine.getCopyOfAuthorityWithSyncStatus( 1569 new SyncStorageEngine.EndPoint( 1570 account.account, 1571 syncAdapterType.type.authority, 1572 account.userId)); 1573 SyncStorageEngine.AuthorityInfo settings = syncAuthoritySyncStatus.first; 1574 SyncStatusInfo status = syncAuthoritySyncStatus.second; 1575 String authority = settings.target.provider; 1576 if (authority.length() > 50) { 1577 authority = authority.substring(authority.length() - 50); 1578 } 1579 table.set(row, 0, authority, settings.syncable, settings.enabled); 1580 table.set(row, 4, 1581 status.numSourceLocal, 1582 status.numSourcePoll, 1583 status.numSourcePeriodic, 1584 status.numSourceServer, 1585 status.numSourceUser, 1586 status.numSyncs, 1587 DateUtils.formatElapsedTime(status.totalElapsedTime / 1000)); 1588 1589 1590 for (int i = 0; i < settings.periodicSyncs.size(); i++) { 1591 final PeriodicSync sync = settings.periodicSyncs.get(i); 1592 final String period = 1593 String.format("[p:%d s, f: %d s]", sync.period, sync.flexTime); 1594 final String extras = 1595 sync.extras.size() > 0 ? 1596 sync.extras.toString() : "Bundle[]"; 1597 final String next = "Next sync: " + formatTime(status.getPeriodicSyncTime(i) 1598 + sync.period * 1000); 1599 table.set(row + i * 2, 12, period + " " + extras); 1600 table.set(row + i * 2 + 1, 12, next); 1601 } 1602 1603 int row1 = row; 1604 if (settings.delayUntil > now) { 1605 table.set(row1++, 12, "D: " + (settings.delayUntil - now) / 1000); 1606 if (settings.backoffTime > now) { 1607 table.set(row1++, 12, "B: " + (settings.backoffTime - now) / 1000); 1608 table.set(row1++, 12, settings.backoffDelay / 1000); 1609 } 1610 } 1611 1612 if (status.lastSuccessTime != 0) { 1613 table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastSuccessSource] 1614 + " " + "SUCCESS"); 1615 table.set(row1++, 11, formatTime(status.lastSuccessTime)); 1616 } 1617 if (status.lastFailureTime != 0) { 1618 table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastFailureSource] 1619 + " " + "FAILURE"); 1620 table.set(row1++, 11, formatTime(status.lastFailureTime)); 1621 //noinspection UnusedAssignment 1622 table.set(row1++, 11, status.lastFailureMesg); 1623 } 1624 } 1625 table.writeTo(pw); 1626 } 1627 } 1628 1629 private String getLastFailureMessage(int code) { 1630 switch (code) { 1631 case ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS: 1632 return "sync already in progress"; 1633 1634 case ContentResolver.SYNC_ERROR_AUTHENTICATION: 1635 return "authentication error"; 1636 1637 case ContentResolver.SYNC_ERROR_IO: 1638 return "I/O error"; 1639 1640 case ContentResolver.SYNC_ERROR_PARSE: 1641 return "parse error"; 1642 1643 case ContentResolver.SYNC_ERROR_CONFLICT: 1644 return "conflict error"; 1645 1646 case ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS: 1647 return "too many deletions error"; 1648 1649 case ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES: 1650 return "too many retries error"; 1651 1652 case ContentResolver.SYNC_ERROR_INTERNAL: 1653 return "internal error"; 1654 1655 default: 1656 return "unknown"; 1657 } 1658 } 1659 1660 private void dumpTimeSec(PrintWriter pw, long time) { 1661 pw.print(time/1000); pw.print('.'); pw.print((time/100)%10); 1662 pw.print('s'); 1663 } 1664 1665 private void dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds) { 1666 pw.print("Success ("); pw.print(ds.successCount); 1667 if (ds.successCount > 0) { 1668 pw.print(" for "); dumpTimeSec(pw, ds.successTime); 1669 pw.print(" avg="); dumpTimeSec(pw, ds.successTime/ds.successCount); 1670 } 1671 pw.print(") Failure ("); pw.print(ds.failureCount); 1672 if (ds.failureCount > 0) { 1673 pw.print(" for "); dumpTimeSec(pw, ds.failureTime); 1674 pw.print(" avg="); dumpTimeSec(pw, ds.failureTime/ds.failureCount); 1675 } 1676 pw.println(")"); 1677 } 1678 1679 protected void dumpSyncHistory(PrintWriter pw) { 1680 dumpRecentHistory(pw); 1681 dumpDayStatistics(pw); 1682 } 1683 1684 private void dumpRecentHistory(PrintWriter pw) { 1685 final ArrayList<SyncStorageEngine.SyncHistoryItem> items 1686 = mSyncStorageEngine.getSyncHistory(); 1687 if (items != null && items.size() > 0) { 1688 final Map<String, AuthoritySyncStats> authorityMap = Maps.newHashMap(); 1689 long totalElapsedTime = 0; 1690 long totalTimes = 0; 1691 final int N = items.size(); 1692 1693 int maxAuthority = 0; 1694 int maxAccount = 0; 1695 for (SyncStorageEngine.SyncHistoryItem item : items) { 1696 SyncStorageEngine.AuthorityInfo authorityInfo 1697 = mSyncStorageEngine.getAuthority(item.authorityId); 1698 final String authorityName; 1699 final String accountKey; 1700 if (authorityInfo != null) { 1701 if (authorityInfo.target.target_provider) { 1702 authorityName = authorityInfo.target.provider; 1703 accountKey = authorityInfo.target.account.name + "/" 1704 + authorityInfo.target.account.type 1705 + " u" + authorityInfo.target.userId; 1706 } else if (authorityInfo.target.target_service) { 1707 authorityName = authorityInfo.target.service.getPackageName() + "/" 1708 + authorityInfo.target.service.getClassName() 1709 + " u" + authorityInfo.target.userId; 1710 accountKey = "no account"; 1711 } else { 1712 authorityName = "Unknown"; 1713 accountKey = "Unknown"; 1714 } 1715 } else { 1716 authorityName = "Unknown"; 1717 accountKey = "Unknown"; 1718 } 1719 1720 int length = authorityName.length(); 1721 if (length > maxAuthority) { 1722 maxAuthority = length; 1723 } 1724 length = accountKey.length(); 1725 if (length > maxAccount) { 1726 maxAccount = length; 1727 } 1728 1729 final long elapsedTime = item.elapsedTime; 1730 totalElapsedTime += elapsedTime; 1731 totalTimes++; 1732 AuthoritySyncStats authoritySyncStats = authorityMap.get(authorityName); 1733 if (authoritySyncStats == null) { 1734 authoritySyncStats = new AuthoritySyncStats(authorityName); 1735 authorityMap.put(authorityName, authoritySyncStats); 1736 } 1737 authoritySyncStats.elapsedTime += elapsedTime; 1738 authoritySyncStats.times++; 1739 final Map<String, AccountSyncStats> accountMap = authoritySyncStats.accountMap; 1740 AccountSyncStats accountSyncStats = accountMap.get(accountKey); 1741 if (accountSyncStats == null) { 1742 accountSyncStats = new AccountSyncStats(accountKey); 1743 accountMap.put(accountKey, accountSyncStats); 1744 } 1745 accountSyncStats.elapsedTime += elapsedTime; 1746 accountSyncStats.times++; 1747 1748 } 1749 1750 if (totalElapsedTime > 0) { 1751 pw.println(); 1752 pw.printf("Detailed Statistics (Recent history): " 1753 + "%d (# of times) %ds (sync time)\n", 1754 totalTimes, totalElapsedTime / 1000); 1755 1756 final List<AuthoritySyncStats> sortedAuthorities = 1757 new ArrayList<AuthoritySyncStats>(authorityMap.values()); 1758 Collections.sort(sortedAuthorities, new Comparator<AuthoritySyncStats>() { 1759 @Override 1760 public int compare(AuthoritySyncStats lhs, AuthoritySyncStats rhs) { 1761 // reverse order 1762 int compare = Integer.compare(rhs.times, lhs.times); 1763 if (compare == 0) { 1764 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime); 1765 } 1766 return compare; 1767 } 1768 }); 1769 1770 final int maxLength = Math.max(maxAuthority, maxAccount + 3); 1771 final int padLength = 2 + 2 + maxLength + 2 + 10 + 11; 1772 final char chars[] = new char[padLength]; 1773 Arrays.fill(chars, '-'); 1774 final String separator = new String(chars); 1775 1776 final String authorityFormat = 1777 String.format(" %%-%ds: %%-9s %%-11s\n", maxLength + 2); 1778 final String accountFormat = 1779 String.format(" %%-%ds: %%-9s %%-11s\n", maxLength); 1780 1781 pw.println(separator); 1782 for (AuthoritySyncStats authoritySyncStats : sortedAuthorities) { 1783 String name = authoritySyncStats.name; 1784 long elapsedTime; 1785 int times; 1786 String timeStr; 1787 String timesStr; 1788 1789 elapsedTime = authoritySyncStats.elapsedTime; 1790 times = authoritySyncStats.times; 1791 timeStr = String.format("%ds/%d%%", 1792 elapsedTime / 1000, 1793 elapsedTime * 100 / totalElapsedTime); 1794 timesStr = String.format("%d/%d%%", 1795 times, 1796 times * 100 / totalTimes); 1797 pw.printf(authorityFormat, name, timesStr, timeStr); 1798 1799 final List<AccountSyncStats> sortedAccounts = 1800 new ArrayList<AccountSyncStats>( 1801 authoritySyncStats.accountMap.values()); 1802 Collections.sort(sortedAccounts, new Comparator<AccountSyncStats>() { 1803 @Override 1804 public int compare(AccountSyncStats lhs, AccountSyncStats rhs) { 1805 // reverse order 1806 int compare = Integer.compare(rhs.times, lhs.times); 1807 if (compare == 0) { 1808 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime); 1809 } 1810 return compare; 1811 } 1812 }); 1813 for (AccountSyncStats stats: sortedAccounts) { 1814 elapsedTime = stats.elapsedTime; 1815 times = stats.times; 1816 timeStr = String.format("%ds/%d%%", 1817 elapsedTime / 1000, 1818 elapsedTime * 100 / totalElapsedTime); 1819 timesStr = String.format("%d/%d%%", 1820 times, 1821 times * 100 / totalTimes); 1822 pw.printf(accountFormat, stats.name, timesStr, timeStr); 1823 } 1824 pw.println(separator); 1825 } 1826 } 1827 1828 pw.println(); 1829 pw.println("Recent Sync History"); 1830 final String format = " %-" + maxAccount + "s %-" + maxAuthority + "s %s\n"; 1831 final Map<String, Long> lastTimeMap = Maps.newHashMap(); 1832 final PackageManager pm = mContext.getPackageManager(); 1833 for (int i = 0; i < N; i++) { 1834 SyncStorageEngine.SyncHistoryItem item = items.get(i); 1835 SyncStorageEngine.AuthorityInfo authorityInfo 1836 = mSyncStorageEngine.getAuthority(item.authorityId); 1837 final String authorityName; 1838 final String accountKey; 1839 if (authorityInfo != null) { 1840 if (authorityInfo.target.target_provider) { 1841 authorityName = authorityInfo.target.provider; 1842 accountKey = authorityInfo.target.account.name + "/" 1843 + authorityInfo.target.account.type 1844 + " u" + authorityInfo.target.userId; 1845 } else if (authorityInfo.target.target_service) { 1846 authorityName = authorityInfo.target.service.getPackageName() + "/" 1847 + authorityInfo.target.service.getClassName() 1848 + " u" + authorityInfo.target.userId; 1849 accountKey = "none"; 1850 } else { 1851 authorityName = "Unknown"; 1852 accountKey = "Unknown"; 1853 } 1854 } else { 1855 authorityName = "Unknown"; 1856 accountKey = "Unknown"; 1857 } 1858 final long elapsedTime = item.elapsedTime; 1859 final Time time = new Time(); 1860 final long eventTime = item.eventTime; 1861 time.set(eventTime); 1862 1863 final String key = authorityName + "/" + accountKey; 1864 final Long lastEventTime = lastTimeMap.get(key); 1865 final String diffString; 1866 if (lastEventTime == null) { 1867 diffString = ""; 1868 } else { 1869 final long diff = (lastEventTime - eventTime) / 1000; 1870 if (diff < 60) { 1871 diffString = String.valueOf(diff); 1872 } else if (diff < 3600) { 1873 diffString = String.format("%02d:%02d", diff / 60, diff % 60); 1874 } else { 1875 final long sec = diff % 3600; 1876 diffString = String.format("%02d:%02d:%02d", 1877 diff / 3600, sec / 60, sec % 60); 1878 } 1879 } 1880 lastTimeMap.put(key, eventTime); 1881 1882 pw.printf(" #%-3d: %s %8s %5.1fs %8s", 1883 i + 1, 1884 formatTime(eventTime), 1885 SyncStorageEngine.SOURCES[item.source], 1886 ((float) elapsedTime) / 1000, 1887 diffString); 1888 pw.printf(format, accountKey, authorityName, 1889 SyncOperation.reasonToString(pm, item.reason)); 1890 1891 if (item.event != SyncStorageEngine.EVENT_STOP 1892 || item.upstreamActivity != 0 1893 || item.downstreamActivity != 0) { 1894 pw.printf(" event=%d upstreamActivity=%d downstreamActivity=%d\n", 1895 item.event, 1896 item.upstreamActivity, 1897 item.downstreamActivity); 1898 } 1899 if (item.mesg != null 1900 && !SyncStorageEngine.MESG_SUCCESS.equals(item.mesg)) { 1901 pw.printf(" mesg=%s\n", item.mesg); 1902 } 1903 } 1904 pw.println(); 1905 pw.println("Recent Sync History Extras"); 1906 for (int i = 0; i < N; i++) { 1907 final SyncStorageEngine.SyncHistoryItem item = items.get(i); 1908 final Bundle extras = item.extras; 1909 if (extras == null || extras.size() == 0) { 1910 continue; 1911 } 1912 final SyncStorageEngine.AuthorityInfo authorityInfo 1913 = mSyncStorageEngine.getAuthority(item.authorityId); 1914 final String authorityName; 1915 final String accountKey; 1916 if (authorityInfo != null) { 1917 if (authorityInfo.target.target_provider) { 1918 authorityName = authorityInfo.target.provider; 1919 accountKey = authorityInfo.target.account.name + "/" 1920 + authorityInfo.target.account.type 1921 + " u" + authorityInfo.target.userId; 1922 } else if (authorityInfo.target.target_service) { 1923 authorityName = authorityInfo.target.service.getPackageName() + "/" 1924 + authorityInfo.target.service.getClassName() 1925 + " u" + authorityInfo.target.userId; 1926 accountKey = "none"; 1927 } else { 1928 authorityName = "Unknown"; 1929 accountKey = "Unknown"; 1930 } 1931 } else { 1932 authorityName = "Unknown"; 1933 accountKey = "Unknown"; 1934 } 1935 final Time time = new Time(); 1936 final long eventTime = item.eventTime; 1937 time.set(eventTime); 1938 1939 pw.printf(" #%-3d: %s %8s ", 1940 i + 1, 1941 formatTime(eventTime), 1942 SyncStorageEngine.SOURCES[item.source]); 1943 1944 pw.printf(format, accountKey, authorityName, extras); 1945 } 1946 } 1947 } 1948 1949 private void dumpDayStatistics(PrintWriter pw) { 1950 SyncStorageEngine.DayStats dses[] = mSyncStorageEngine.getDayStatistics(); 1951 if (dses != null && dses[0] != null) { 1952 pw.println(); 1953 pw.println("Sync Statistics"); 1954 pw.print(" Today: "); dumpDayStatistic(pw, dses[0]); 1955 int today = dses[0].day; 1956 int i; 1957 SyncStorageEngine.DayStats ds; 1958 1959 // Print each day in the current week. 1960 for (i=1; i<=6 && i < dses.length; i++) { 1961 ds = dses[i]; 1962 if (ds == null) break; 1963 int delta = today-ds.day; 1964 if (delta > 6) break; 1965 1966 pw.print(" Day-"); pw.print(delta); pw.print(": "); 1967 dumpDayStatistic(pw, ds); 1968 } 1969 1970 // Aggregate all following days into weeks and print totals. 1971 int weekDay = today; 1972 while (i < dses.length) { 1973 SyncStorageEngine.DayStats aggr = null; 1974 weekDay -= 7; 1975 while (i < dses.length) { 1976 ds = dses[i]; 1977 if (ds == null) { 1978 i = dses.length; 1979 break; 1980 } 1981 int delta = weekDay-ds.day; 1982 if (delta > 6) break; 1983 i++; 1984 1985 if (aggr == null) { 1986 aggr = new SyncStorageEngine.DayStats(weekDay); 1987 } 1988 aggr.successCount += ds.successCount; 1989 aggr.successTime += ds.successTime; 1990 aggr.failureCount += ds.failureCount; 1991 aggr.failureTime += ds.failureTime; 1992 } 1993 if (aggr != null) { 1994 pw.print(" Week-"); pw.print((today-weekDay)/7); pw.print(": "); 1995 dumpDayStatistic(pw, aggr); 1996 } 1997 } 1998 } 1999 } 2000 2001 private void dumpSyncAdapters(IndentingPrintWriter pw) { 2002 pw.println(); 2003 final List<UserInfo> users = getAllUsers(); 2004 if (users != null) { 2005 for (UserInfo user : users) { 2006 pw.println("Sync adapters for " + user + ":"); 2007 pw.increaseIndent(); 2008 for (RegisteredServicesCache.ServiceInfo<?> info : 2009 mSyncAdapters.getAllServices(user.id)) { 2010 pw.println(info); 2011 } 2012 pw.decreaseIndent(); 2013 pw.println(); 2014 } 2015 } 2016 } 2017 2018 private static class AuthoritySyncStats { 2019 String name; 2020 long elapsedTime; 2021 int times; 2022 Map<String, AccountSyncStats> accountMap = Maps.newHashMap(); 2023 2024 private AuthoritySyncStats(String name) { 2025 this.name = name; 2026 } 2027 } 2028 2029 private static class AccountSyncStats { 2030 String name; 2031 long elapsedTime; 2032 int times; 2033 2034 private AccountSyncStats(String name) { 2035 this.name = name; 2036 } 2037 } 2038 2039 /** 2040 * A helper object to keep track of the time we have spent syncing since the last boot 2041 */ 2042 private class SyncTimeTracker { 2043 /** True if a sync was in progress on the most recent call to update() */ 2044 boolean mLastWasSyncing = false; 2045 /** Used to track when lastWasSyncing was last set */ 2046 long mWhenSyncStarted = 0; 2047 /** The cumulative time we have spent syncing */ 2048 private long mTimeSpentSyncing; 2049 2050 /** Call to let the tracker know that the sync state may have changed */ 2051 public synchronized void update() { 2052 final boolean isSyncInProgress = !mActiveSyncContexts.isEmpty(); 2053 if (isSyncInProgress == mLastWasSyncing) return; 2054 final long now = SystemClock.elapsedRealtime(); 2055 if (isSyncInProgress) { 2056 mWhenSyncStarted = now; 2057 } else { 2058 mTimeSpentSyncing += now - mWhenSyncStarted; 2059 } 2060 mLastWasSyncing = isSyncInProgress; 2061 } 2062 2063 /** Get how long we have been syncing, in ms */ 2064 public synchronized long timeSpentSyncing() { 2065 if (!mLastWasSyncing) return mTimeSpentSyncing; 2066 2067 final long now = SystemClock.elapsedRealtime(); 2068 return mTimeSpentSyncing + (now - mWhenSyncStarted); 2069 } 2070 } 2071 2072 class ServiceConnectionData { 2073 public final ActiveSyncContext activeSyncContext; 2074 public final IBinder adapter; 2075 2076 ServiceConnectionData(ActiveSyncContext activeSyncContext, IBinder adapter) { 2077 this.activeSyncContext = activeSyncContext; 2078 this.adapter = adapter; 2079 } 2080 } 2081 2082 /** 2083 * Handles SyncOperation Messages that are posted to the associated 2084 * HandlerThread. 2085 */ 2086 class SyncHandler extends Handler { 2087 // Messages that can be sent on mHandler 2088 private static final int MESSAGE_SYNC_FINISHED = 1; 2089 private static final int MESSAGE_SYNC_ALARM = 2; 2090 private static final int MESSAGE_CHECK_ALARMS = 3; 2091 private static final int MESSAGE_SERVICE_CONNECTED = 4; 2092 private static final int MESSAGE_SERVICE_DISCONNECTED = 5; 2093 private static final int MESSAGE_CANCEL = 6; 2094 /** 2095 * Posted delayed in order to expire syncs that are long-running. 2096 * obj: {@link com.android.server.content.SyncManager.ActiveSyncContext} 2097 */ 2098 private static final int MESSAGE_SYNC_EXPIRED = 7; 2099 /** 2100 * Posted periodically to monitor network process for long-running syncs. 2101 * obj: {@link com.android.server.content.SyncManager.ActiveSyncContext} 2102 */ 2103 private static final int MESSAGE_MONITOR_SYNC = 8; 2104 2105 public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo(); 2106 private Long mAlarmScheduleTime = null; 2107 public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker(); 2108 private final HashMap<String, PowerManager.WakeLock> mWakeLocks = Maps.newHashMap(); 2109 2110 private List<Message> mUnreadyQueue = new ArrayList<Message>(); 2111 2112 void onBootCompleted() { 2113 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2114 Log.v(TAG, "Boot completed, clearing boot queue."); 2115 } 2116 doDatabaseCleanup(); 2117 synchronized(this) { 2118 // Dispatch any stashed messages. 2119 maybeEmptyUnreadyQueueLocked(); 2120 } 2121 } 2122 2123 void onDeviceProvisioned() { 2124 if (Log.isLoggable(TAG, Log.DEBUG)) { 2125 Log.d(TAG, "mProvisioned=" + mProvisioned); 2126 } 2127 synchronized (this) { 2128 maybeEmptyUnreadyQueueLocked(); 2129 } 2130 } 2131 2132 private void maybeEmptyUnreadyQueueLocked() { 2133 if (mProvisioned && mBootCompleted) { 2134 // Dispatch any stashed messages. 2135 for (int i=0; i<mUnreadyQueue.size(); i++) { 2136 sendMessageDelayed(mUnreadyQueue.get(i), 2137 Math.max(PER_SYNC_BOOT_DELAY_MILLIS * i, MAX_SYNC_BOOT_DELAY_MILLIS)); 2138 } 2139 mUnreadyQueue = null; 2140 } 2141 } 2142 2143 private PowerManager.WakeLock getSyncWakeLock(SyncOperation operation) { 2144 final String wakeLockKey = operation.wakeLockName(); 2145 PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey); 2146 if (wakeLock == null) { 2147 final String name = SYNC_WAKE_LOCK_PREFIX + wakeLockKey; 2148 wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name); 2149 wakeLock.setReferenceCounted(false); 2150 mWakeLocks.put(wakeLockKey, wakeLock); 2151 } 2152 return wakeLock; 2153 } 2154 2155 /** 2156 * Stash any messages that come to the handler before boot is complete or before the device 2157 * is properly provisioned (i.e. out of set-up wizard). 2158 * {@link #onBootCompleted()} and {@link #onDeviceProvisioned(boolean)} both need to come 2159 * in before we start syncing. 2160 * @param msg Message to dispatch at a later point. 2161 * @return true if a message was enqueued, false otherwise. This is to avoid losing the 2162 * message if we manage to acquire the lock but by the time we do boot has completed. 2163 */ 2164 private boolean tryEnqueueMessageUntilReadyToRun(Message msg) { 2165 synchronized (this) { 2166 if (!mBootCompleted || !mProvisioned) { 2167 // Need to copy the message bc looper will recycle it. 2168 mUnreadyQueue.add(Message.obtain(msg)); 2169 return true; 2170 } else { 2171 return false; 2172 } 2173 } 2174 } 2175 2176 /** 2177 * Used to keep track of whether a sync notification is active and who it is for. 2178 */ 2179 class SyncNotificationInfo { 2180 // true iff the notification manager has been asked to send the notification 2181 public boolean isActive = false; 2182 2183 // Set when we transition from not running a sync to running a sync, and cleared on 2184 // the opposite transition. 2185 public Long startTime = null; 2186 2187 public void toString(StringBuilder sb) { 2188 sb.append("isActive ").append(isActive).append(", startTime ").append(startTime); 2189 } 2190 2191 @Override 2192 public String toString() { 2193 StringBuilder sb = new StringBuilder(); 2194 toString(sb); 2195 return sb.toString(); 2196 } 2197 } 2198 2199 public SyncHandler(Looper looper) { 2200 super(looper); 2201 } 2202 2203 public void handleMessage(Message msg) { 2204 if (tryEnqueueMessageUntilReadyToRun(msg)) { 2205 return; 2206 } 2207 2208 long earliestFuturePollTime = Long.MAX_VALUE; 2209 long nextPendingSyncTime = Long.MAX_VALUE; 2210 // Setting the value here instead of a method because we want the dumpsys logs 2211 // to have the most recent value used. 2212 try { 2213 mDataConnectionIsConnected = readDataConnectionState(); 2214 mSyncManagerWakeLock.acquire(); 2215 // Always do this first so that we be sure that any periodic syncs that 2216 // are ready to run have been converted into pending syncs. This allows the 2217 // logic that considers the next steps to take based on the set of pending syncs 2218 // to also take into account the periodic syncs. 2219 earliestFuturePollTime = scheduleReadyPeriodicSyncs(); 2220 switch (msg.what) { 2221 case SyncHandler.MESSAGE_CANCEL: 2222 SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj; 2223 Bundle extras = msg.peekData(); 2224 if (Log.isLoggable(TAG, Log.DEBUG)) { 2225 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_CANCEL: " 2226 + endpoint + " bundle: " + extras); 2227 } 2228 cancelActiveSyncH(endpoint, extras); 2229 nextPendingSyncTime = maybeStartNextSyncH(); 2230 break; 2231 2232 case SyncHandler.MESSAGE_SYNC_FINISHED: 2233 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2234 Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_FINISHED"); 2235 } 2236 SyncHandlerMessagePayload payload = (SyncHandlerMessagePayload) msg.obj; 2237 if (!isSyncStillActiveH(payload.activeSyncContext)) { 2238 Log.d(TAG, "handleSyncHandlerMessage: dropping since the " 2239 + "sync is no longer active: " 2240 + payload.activeSyncContext); 2241 break; 2242 } 2243 runSyncFinishedOrCanceledH(payload.syncResult, 2244 payload.activeSyncContext); 2245 2246 // since a sync just finished check if it is time to start a new sync 2247 nextPendingSyncTime = maybeStartNextSyncH(); 2248 break; 2249 2250 case SyncHandler.MESSAGE_SERVICE_CONNECTED: { 2251 ServiceConnectionData msgData = (ServiceConnectionData) msg.obj; 2252 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2253 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: " 2254 + msgData.activeSyncContext); 2255 } 2256 // check that this isn't an old message 2257 if (isSyncStillActiveH(msgData.activeSyncContext)) { 2258 runBoundToAdapter( 2259 msgData.activeSyncContext, 2260 msgData.adapter); 2261 } 2262 break; 2263 } 2264 2265 case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: { 2266 final ActiveSyncContext currentSyncContext = 2267 ((ServiceConnectionData) msg.obj).activeSyncContext; 2268 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2269 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: " 2270 + currentSyncContext); 2271 } 2272 // check that this isn't an old message 2273 if (isSyncStillActiveH(currentSyncContext)) { 2274 // cancel the sync if we have a syncadapter, which means one is 2275 // outstanding 2276 try { 2277 if (currentSyncContext.mSyncAdapter != null) { 2278 currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext); 2279 } else if (currentSyncContext.mSyncServiceAdapter != null) { 2280 currentSyncContext.mSyncServiceAdapter 2281 .cancelSync(currentSyncContext); 2282 } 2283 } catch (RemoteException e) { 2284 // We don't need to retry this in this case. 2285 } 2286 2287 // pretend that the sync failed with an IOException, 2288 // which is a soft error 2289 SyncResult syncResult = new SyncResult(); 2290 syncResult.stats.numIoExceptions++; 2291 runSyncFinishedOrCanceledH(syncResult, currentSyncContext); 2292 2293 // since a sync just finished check if it is time to start a new sync 2294 nextPendingSyncTime = maybeStartNextSyncH(); 2295 } 2296 break; 2297 } 2298 2299 case SyncHandler.MESSAGE_SYNC_ALARM: { 2300 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2301 if (isLoggable) { 2302 Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_ALARM"); 2303 } 2304 mAlarmScheduleTime = null; 2305 try { 2306 nextPendingSyncTime = maybeStartNextSyncH(); 2307 } finally { 2308 mHandleAlarmWakeLock.release(); 2309 } 2310 break; 2311 } 2312 2313 case SyncHandler.MESSAGE_CHECK_ALARMS: 2314 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2315 Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_CHECK_ALARMS"); 2316 } 2317 nextPendingSyncTime = maybeStartNextSyncH(); 2318 break; 2319 case SyncHandler.MESSAGE_SYNC_EXPIRED: 2320 ActiveSyncContext expiredContext = (ActiveSyncContext) msg.obj; 2321 if (Log.isLoggable(TAG, Log.DEBUG)) { 2322 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_EXPIRED:" + 2323 " cancelling " + expiredContext); 2324 } 2325 runSyncFinishedOrCanceledH( 2326 null /* cancel => no result */, 2327 expiredContext); 2328 nextPendingSyncTime = maybeStartNextSyncH(); 2329 break; 2330 case SyncHandler.MESSAGE_MONITOR_SYNC: 2331 ActiveSyncContext monitoredSyncContext = (ActiveSyncContext) msg.obj; 2332 if (Log.isLoggable(TAG, Log.DEBUG)) { 2333 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_MONITOR_SYNC: " + 2334 monitoredSyncContext.mSyncOperation.target); 2335 } 2336 2337 if (isSyncNotUsingNetworkH(monitoredSyncContext)) { 2338 Log.w(TAG, String.format( 2339 "Detected sync making no progress for %s. cancelling.", 2340 monitoredSyncContext)); 2341 runSyncFinishedOrCanceledH( 2342 null /* cancel => no result */, monitoredSyncContext); 2343 } else { 2344 // Repost message to check again. 2345 postMonitorSyncProgressMessage(monitoredSyncContext); 2346 } 2347 break; 2348 2349 } 2350 } finally { 2351 manageSyncNotificationLocked(); 2352 manageSyncAlarmLocked(earliestFuturePollTime, nextPendingSyncTime); 2353 mSyncTimeTracker.update(); 2354 mSyncManagerWakeLock.release(); 2355 } 2356 } 2357 2358 private boolean isDispatchable(SyncStorageEngine.EndPoint target) { 2359 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2360 if (target.target_provider) { 2361 // skip the sync if the account of this operation no longer exists 2362 AccountAndUser[] accounts = mRunningAccounts; 2363 if (!containsAccountAndUser( 2364 accounts, target.account, target.userId)) { 2365 return false; 2366 } 2367 if (!mSyncStorageEngine.getMasterSyncAutomatically(target.userId) 2368 || !mSyncStorageEngine.getSyncAutomatically( 2369 target.account, 2370 target.userId, 2371 target.provider)) { 2372 if (isLoggable) { 2373 Log.v(TAG, " Not scheduling periodic operation: sync turned off."); 2374 } 2375 return false; 2376 } 2377 if (getIsSyncable(target.account, target.userId, target.provider) 2378 == 0) { 2379 if (isLoggable) { 2380 Log.v(TAG, " Not scheduling periodic operation: isSyncable == 0."); 2381 } 2382 return false; 2383 } 2384 } else if (target.target_service) { 2385 if (mSyncStorageEngine.getIsTargetServiceActive(target.service, target.userId)) { 2386 if (isLoggable) { 2387 Log.v(TAG, " Not scheduling periodic operation: isEnabled == 0."); 2388 } 2389 return false; 2390 } 2391 } 2392 return true; 2393 } 2394 2395 /** 2396 * Turn any periodic sync operations that are ready to run into pending sync operations. 2397 * @return the desired start time of the earliest future periodic sync operation, 2398 * in milliseconds since boot 2399 */ 2400 private long scheduleReadyPeriodicSyncs() { 2401 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2402 if (isLoggable) { 2403 Log.v(TAG, "scheduleReadyPeriodicSyncs"); 2404 } 2405 long earliestFuturePollTime = Long.MAX_VALUE; 2406 2407 final long nowAbsolute = System.currentTimeMillis(); 2408 final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis) 2409 ? (nowAbsolute - mSyncRandomOffsetMillis) : 0; 2410 2411 ArrayList<Pair<AuthorityInfo, SyncStatusInfo>> infos = mSyncStorageEngine 2412 .getCopyOfAllAuthoritiesWithSyncStatus(); 2413 for (Pair<AuthorityInfo, SyncStatusInfo> info : infos) { 2414 final AuthorityInfo authorityInfo = info.first; 2415 final SyncStatusInfo status = info.second; 2416 2417 if (TextUtils.isEmpty(authorityInfo.target.provider)) { 2418 Log.e(TAG, "Got an empty provider string. Skipping: " 2419 + authorityInfo.target.provider); 2420 continue; 2421 } 2422 2423 if (!isDispatchable(authorityInfo.target)) { 2424 continue; 2425 } 2426 2427 for (int i = 0, N = authorityInfo.periodicSyncs.size(); i < N; i++) { 2428 final PeriodicSync sync = authorityInfo.periodicSyncs.get(i); 2429 final Bundle extras = sync.extras; 2430 final Long periodInMillis = sync.period * 1000; 2431 final Long flexInMillis = sync.flexTime * 1000; 2432 // Skip if the period is invalid. 2433 if (periodInMillis <= 0) { 2434 continue; 2435 } 2436 // Find when this periodic sync was last scheduled to run. 2437 final long lastPollTimeAbsolute = status.getPeriodicSyncTime(i); 2438 final long shiftedLastPollTimeAbsolute = 2439 (0 < lastPollTimeAbsolute - mSyncRandomOffsetMillis) ? 2440 (lastPollTimeAbsolute - mSyncRandomOffsetMillis) : 0; 2441 long remainingMillis 2442 = periodInMillis - (shiftedNowAbsolute % periodInMillis); 2443 long timeSinceLastRunMillis 2444 = (nowAbsolute - lastPollTimeAbsolute); 2445 // Schedule this periodic sync to run early if it's close enough to its next 2446 // runtime, and far enough from its last run time. 2447 // If we are early, there will still be time remaining in this period. 2448 boolean runEarly = remainingMillis <= flexInMillis 2449 && timeSinceLastRunMillis > periodInMillis - flexInMillis; 2450 if (isLoggable) { 2451 Log.v(TAG, "sync: " + i + " for " + authorityInfo.target + "." 2452 + " period: " + (periodInMillis) 2453 + " flex: " + (flexInMillis) 2454 + " remaining: " + (remainingMillis) 2455 + " time_since_last: " + timeSinceLastRunMillis 2456 + " last poll absol: " + lastPollTimeAbsolute 2457 + " last poll shifed: " + shiftedLastPollTimeAbsolute 2458 + " shifted now: " + shiftedNowAbsolute 2459 + " run_early: " + runEarly); 2460 } 2461 /* 2462 * Sync scheduling strategy: Set the next periodic sync 2463 * based on a random offset (in seconds). Also sync right 2464 * now if any of the following cases hold and mark it as 2465 * having been scheduled 2466 * Case 1: This sync is ready to run now. 2467 * Case 2: If the lastPollTimeAbsolute is in the 2468 * future, sync now and reinitialize. This can happen for 2469 * example if the user changed the time, synced and changed 2470 * back. 2471 * Case 3: If we failed to sync at the last scheduled time. 2472 * Case 4: This sync is close enough to the time that we can schedule it. 2473 */ 2474 if (remainingMillis == periodInMillis // Case 1 2475 || lastPollTimeAbsolute > nowAbsolute // Case 2 2476 || timeSinceLastRunMillis >= periodInMillis // Case 3 2477 || runEarly) { // Case 4 2478 // Sync now 2479 SyncStorageEngine.EndPoint target = authorityInfo.target; 2480 final Pair<Long, Long> backoff = 2481 mSyncStorageEngine.getBackoff(target); 2482 mSyncStorageEngine.setPeriodicSyncTime(authorityInfo.ident, 2483 authorityInfo.periodicSyncs.get(i), nowAbsolute); 2484 2485 if (target.target_provider) { 2486 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> 2487 syncAdapterInfo = mSyncAdapters.getServiceInfo( 2488 SyncAdapterType.newKey( 2489 target.provider, target.account.type), 2490 target.userId); 2491 if (syncAdapterInfo == null) { 2492 continue; 2493 } 2494 scheduleSyncOperation( 2495 new SyncOperation(target.account, target.userId, 2496 SyncOperation.REASON_PERIODIC, 2497 SyncStorageEngine.SOURCE_PERIODIC, 2498 target.provider, extras, 2499 0 /* runtime */, 0 /* flex */, 2500 backoff != null ? backoff.first : 0, 2501 mSyncStorageEngine.getDelayUntilTime(target), 2502 syncAdapterInfo.type.allowParallelSyncs())); 2503 } else if (target.target_service) { 2504 scheduleSyncOperation( 2505 new SyncOperation(target.service, target.userId, 2506 SyncOperation.REASON_PERIODIC, 2507 SyncStorageEngine.SOURCE_PERIODIC, 2508 extras, 2509 0 /* runtime */, 2510 0 /* flex */, 2511 backoff != null ? backoff.first : 0, 2512 mSyncStorageEngine.getDelayUntilTime(target))); 2513 } 2514 } 2515 // Compute when this periodic sync should next run. 2516 long nextPollTimeAbsolute; 2517 if (runEarly) { 2518 // Add the time remaining so we don't get out of phase. 2519 nextPollTimeAbsolute = nowAbsolute + periodInMillis + remainingMillis; 2520 } else { 2521 nextPollTimeAbsolute = nowAbsolute + remainingMillis; 2522 } 2523 if (nextPollTimeAbsolute < earliestFuturePollTime) { 2524 earliestFuturePollTime = nextPollTimeAbsolute; 2525 } 2526 } 2527 } 2528 2529 if (earliestFuturePollTime == Long.MAX_VALUE) { 2530 return Long.MAX_VALUE; 2531 } 2532 2533 // convert absolute time to elapsed time 2534 return SystemClock.elapsedRealtime() + 2535 ((earliestFuturePollTime < nowAbsolute) ? 2536 0 : (earliestFuturePollTime - nowAbsolute)); 2537 } 2538 2539 private long maybeStartNextSyncH() { 2540 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2541 if (isLoggable) Log.v(TAG, "maybeStartNextSync"); 2542 2543 // If we aren't ready to run (e.g. the data connection is down), get out. 2544 if (!mDataConnectionIsConnected) { 2545 if (isLoggable) { 2546 Log.v(TAG, "maybeStartNextSync: no data connection, skipping"); 2547 } 2548 return Long.MAX_VALUE; 2549 } 2550 2551 if (mStorageIsLow) { 2552 if (isLoggable) { 2553 Log.v(TAG, "maybeStartNextSync: memory low, skipping"); 2554 } 2555 return Long.MAX_VALUE; 2556 } 2557 2558 if (mDeviceIsIdle) { 2559 if (isLoggable) { 2560 Log.v(TAG, "maybeStartNextSync: device idle, skipping"); 2561 } 2562 return Long.MAX_VALUE; 2563 } 2564 2565 // If the accounts aren't known yet then we aren't ready to run. We will be kicked 2566 // when the account lookup request does complete. 2567 if (mRunningAccounts == INITIAL_ACCOUNTS_ARRAY) { 2568 if (isLoggable) { 2569 Log.v(TAG, "maybeStartNextSync: accounts not known, skipping"); 2570 } 2571 return Long.MAX_VALUE; 2572 } 2573 2574 // Otherwise consume SyncOperations from the head of the SyncQueue until one is 2575 // found that is runnable (not disabled, etc). If that one is ready to run then 2576 // start it, otherwise just get out. 2577 final long now = SystemClock.elapsedRealtime(); 2578 2579 // will be set to the next time that a sync should be considered for running 2580 long nextReadyToRunTime = Long.MAX_VALUE; 2581 2582 // order the sync queue, dropping syncs that are not allowed 2583 ArrayList<SyncOperation> operations = new ArrayList<SyncOperation>(); 2584 synchronized (mSyncQueue) { 2585 if (isLoggable) { 2586 Log.v(TAG, "build the operation array, syncQueue size is " 2587 + mSyncQueue.getOperations().size()); 2588 } 2589 final Iterator<SyncOperation> operationIterator = 2590 mSyncQueue.getOperations().iterator(); 2591 2592 final ActivityManager activityManager 2593 = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); 2594 final Set<Integer> removedUsers = Sets.newHashSet(); 2595 while (operationIterator.hasNext()) { 2596 final SyncOperation op = operationIterator.next(); 2597 2598 // If the user is not running, skip the request. 2599 if (!activityManager.isUserRunning(op.target.userId)) { 2600 final UserInfo userInfo = mUserManager.getUserInfo(op.target.userId); 2601 if (userInfo == null) { 2602 removedUsers.add(op.target.userId); 2603 } 2604 if (isLoggable) { 2605 Log.v(TAG, " Dropping all sync operations for + " 2606 + op.target.userId + ": user not running."); 2607 } 2608 continue; 2609 } 2610 if (!isOperationValidLocked(op)) { 2611 operationIterator.remove(); 2612 mSyncStorageEngine.deleteFromPending(op.pendingOperation); 2613 continue; 2614 } 2615 // If the next run time is in the future, even given the flexible scheduling, 2616 // return the time. 2617 if (op.effectiveRunTime - op.flexTime > now) { 2618 if (nextReadyToRunTime > op.effectiveRunTime) { 2619 nextReadyToRunTime = op.effectiveRunTime; 2620 } 2621 if (isLoggable) { 2622 Log.v(TAG, " Not running sync operation: Sync too far in future." 2623 + "effective: " + op.effectiveRunTime + " flex: " + op.flexTime 2624 + " now: " + now); 2625 } 2626 continue; 2627 } 2628 String packageName = getPackageName(op.target); 2629 // If app is considered idle, then skip for now and backoff 2630 if (packageName != null 2631 && mAppIdleMonitor.isAppIdle(packageName, op.target.userId)) { 2632 increaseBackoffSetting(op); 2633 op.appIdle = true; 2634 if (isLoggable) { 2635 Log.v(TAG, "Sync backing off idle app " + packageName); 2636 } 2637 continue; 2638 } else { 2639 op.appIdle = false; 2640 } 2641 // Add this sync to be run. 2642 operations.add(op); 2643 } 2644 2645 for (Integer user : removedUsers) { 2646 // if it's still removed 2647 if (mUserManager.getUserInfo(user) == null) { 2648 onUserRemoved(user); 2649 } 2650 } 2651 } 2652 2653 // find the next operation to dispatch, if one is ready 2654 // iterate from the top, keep issuing (while potentially canceling existing syncs) 2655 // until the quotas are filled. 2656 // once the quotas are filled iterate once more to find when the next one would be 2657 // (also considering pre-emption reasons). 2658 if (isLoggable) Log.v(TAG, "sort the candidate operations, size " + operations.size()); 2659 Collections.sort(operations); 2660 if (isLoggable) Log.v(TAG, "dispatch all ready sync operations"); 2661 for (int i = 0, N = operations.size(); i < N; i++) { 2662 final SyncOperation candidate = operations.get(i); 2663 final boolean candidateIsInitialization = candidate.isInitialization(); 2664 2665 int numInit = 0; 2666 int numRegular = 0; 2667 ActiveSyncContext conflict = null; 2668 ActiveSyncContext longRunning = null; 2669 ActiveSyncContext toReschedule = null; 2670 ActiveSyncContext oldestNonExpeditedRegular = null; 2671 2672 for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) { 2673 final SyncOperation activeOp = activeSyncContext.mSyncOperation; 2674 if (activeOp.isInitialization()) { 2675 numInit++; 2676 } else { 2677 numRegular++; 2678 if (!activeOp.isExpedited()) { 2679 if (oldestNonExpeditedRegular == null 2680 || (oldestNonExpeditedRegular.mStartTime 2681 > activeSyncContext.mStartTime)) { 2682 oldestNonExpeditedRegular = activeSyncContext; 2683 } 2684 } 2685 } 2686 if (activeOp.isConflict(candidate)) { 2687 conflict = activeSyncContext; 2688 // don't break out since we want to do a full count of the varieties. 2689 } else { 2690 if (candidateIsInitialization == activeOp.isInitialization() 2691 && activeSyncContext.mStartTime + MAX_TIME_PER_SYNC < now) { 2692 longRunning = activeSyncContext; 2693 // don't break out since we want to do a full count of the varieties 2694 } 2695 } 2696 } 2697 2698 if (isLoggable) { 2699 Log.v(TAG, "candidate " + (i + 1) + " of " + N + ": " + candidate); 2700 Log.v(TAG, " numActiveInit=" + numInit + ", numActiveRegular=" + numRegular); 2701 Log.v(TAG, " longRunning: " + longRunning); 2702 Log.v(TAG, " conflict: " + conflict); 2703 Log.v(TAG, " oldestNonExpeditedRegular: " + oldestNonExpeditedRegular); 2704 } 2705 2706 final boolean roomAvailable = candidateIsInitialization 2707 ? numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS 2708 : numRegular < MAX_SIMULTANEOUS_REGULAR_SYNCS; 2709 2710 if (conflict != null) { 2711 if (candidateIsInitialization && !conflict.mSyncOperation.isInitialization() 2712 && numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS) { 2713 toReschedule = conflict; 2714 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2715 Log.v(TAG, "canceling and rescheduling sync since an initialization " 2716 + "takes higher priority, " + conflict); 2717 } 2718 } else if (candidate.isExpedited() && !conflict.mSyncOperation.isExpedited() 2719 && (candidateIsInitialization 2720 == conflict.mSyncOperation.isInitialization())) { 2721 toReschedule = conflict; 2722 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2723 Log.v(TAG, "canceling and rescheduling sync since an expedited " 2724 + "takes higher priority, " + conflict); 2725 } 2726 } else { 2727 continue; 2728 } 2729 } else if (roomAvailable) { 2730 // dispatch candidate 2731 } else if (candidate.isExpedited() && oldestNonExpeditedRegular != null 2732 && !candidateIsInitialization) { 2733 // We found an active, non-expedited regular sync. We also know that the 2734 // candidate doesn't conflict with this active sync since conflict 2735 // is null. Reschedule the active sync and start the candidate. 2736 toReschedule = oldestNonExpeditedRegular; 2737 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2738 Log.v(TAG, "canceling and rescheduling sync since an expedited is ready to" 2739 + " run, " + oldestNonExpeditedRegular); 2740 } 2741 } else if (longRunning != null 2742 && (candidateIsInitialization 2743 == longRunning.mSyncOperation.isInitialization())) { 2744 // We found an active, long-running sync. Reschedule the active 2745 // sync and start the candidate. 2746 toReschedule = longRunning; 2747 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2748 Log.v(TAG, "canceling and rescheduling sync since it ran roo long, " 2749 + longRunning); 2750 } 2751 } else { 2752 // we were unable to find or make space to run this candidate, go on to 2753 // the next one 2754 continue; 2755 } 2756 2757 if (toReschedule != null) { 2758 runSyncFinishedOrCanceledH(null, toReschedule); 2759 scheduleSyncOperation(toReschedule.mSyncOperation); 2760 } 2761 synchronized (mSyncQueue) { 2762 mSyncQueue.remove(candidate); 2763 } 2764 dispatchSyncOperation(candidate); 2765 } 2766 2767 return nextReadyToRunTime; 2768 } 2769 2770 private boolean isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext) { 2771 final long bytesTransferredCurrent = 2772 getTotalBytesTransferredByUid(activeSyncContext.mSyncAdapterUid); 2773 final long deltaBytesTransferred = 2774 bytesTransferredCurrent - activeSyncContext.mBytesTransferredAtLastPoll; 2775 2776 if (Log.isLoggable(TAG, Log.DEBUG)) { 2777 // Bytes transferred 2778 long remainder = deltaBytesTransferred; 2779 long mb = remainder / (1024 * 1024); 2780 remainder %= 1024 * 1024; 2781 long kb = remainder / 1024; 2782 remainder %= 1024; 2783 long b = remainder; 2784 Log.d(TAG, String.format( 2785 "Time since last update: %ds. Delta transferred: %dMBs,%dKBs,%dBs", 2786 (SystemClock.elapsedRealtime() 2787 - activeSyncContext.mLastPolledTimeElapsed)/1000, 2788 mb, kb, b) 2789 ); 2790 } 2791 return (deltaBytesTransferred <= SYNC_MONITOR_PROGRESS_THRESHOLD_BYTES); 2792 } 2793 2794 /** 2795 * Determine if a sync is no longer valid and should be dropped from the sync queue and its 2796 * pending op deleted. 2797 * @param op operation for which the sync is to be scheduled. 2798 */ 2799 private boolean isOperationValidLocked(SyncOperation op) { 2800 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2801 int targetUid; 2802 int state; 2803 final SyncStorageEngine.EndPoint target = op.target; 2804 boolean syncEnabled = mSyncStorageEngine.getMasterSyncAutomatically(target.userId); 2805 if (target.target_provider) { 2806 // Drop the sync if the account of this operation no longer exists. 2807 AccountAndUser[] accounts = mRunningAccounts; 2808 if (!containsAccountAndUser(accounts, target.account, target.userId)) { 2809 if (isLoggable) { 2810 Log.v(TAG, " Dropping sync operation: account doesn't exist."); 2811 } 2812 return false; 2813 } 2814 // Drop this sync request if it isn't syncable. 2815 state = getIsSyncable(target.account, target.userId, target.provider); 2816 if (state == 0) { 2817 if (isLoggable) { 2818 Log.v(TAG, " Dropping sync operation: isSyncable == 0."); 2819 } 2820 return false; 2821 } 2822 syncEnabled = syncEnabled && mSyncStorageEngine.getSyncAutomatically( 2823 target.account, target.userId, target.provider); 2824 2825 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; 2826 syncAdapterInfo = mSyncAdapters.getServiceInfo( 2827 SyncAdapterType.newKey( 2828 target.provider, target.account.type), target.userId); 2829 if (syncAdapterInfo != null) { 2830 targetUid = syncAdapterInfo.uid; 2831 } else { 2832 if (isLoggable) { 2833 Log.v(TAG, " Dropping sync operation: No sync adapter registered" 2834 + "for: " + target); 2835 } 2836 return false; 2837 } 2838 } else if (target.target_service) { 2839 state = mSyncStorageEngine.getIsTargetServiceActive(target.service, target.userId) 2840 ? 1 : 0; 2841 if (state == 0) { 2842 // TODO: Change this to not drop disabled syncs - keep them in the pending queue. 2843 if (isLoggable) { 2844 Log.v(TAG, " Dropping sync operation: isActive == 0."); 2845 } 2846 return false; 2847 } 2848 try { 2849 targetUid = mContext.getPackageManager() 2850 .getServiceInfo(target.service, 0) 2851 .applicationInfo 2852 .uid; 2853 } catch (PackageManager.NameNotFoundException e) { 2854 if (isLoggable) { 2855 Log.v(TAG, " Dropping sync operation: No service registered for: " 2856 + target.service); 2857 } 2858 return false; 2859 } 2860 } else { 2861 Log.e(TAG, "Unknown target for Sync Op: " + target); 2862 return false; 2863 } 2864 2865 // We ignore system settings that specify the sync is invalid if: 2866 // 1) It's manual - we try it anyway. When/if it fails it will be rescheduled. 2867 // or 2868 // 2) it's an initialisation sync - we just need to connect to it. 2869 final boolean ignoreSystemConfiguration = 2870 op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false) 2871 || (state < 0); 2872 2873 // Sync not enabled. 2874 if (!syncEnabled && !ignoreSystemConfiguration) { 2875 if (isLoggable) { 2876 Log.v(TAG, " Dropping sync operation: disallowed by settings/network."); 2877 } 2878 return false; 2879 } 2880 // Network down. 2881 final NetworkInfo networkInfo = getConnectivityManager() 2882 .getActiveNetworkInfoForUid(targetUid); 2883 final boolean uidNetworkConnected = networkInfo != null && networkInfo.isConnected(); 2884 if (!uidNetworkConnected && !ignoreSystemConfiguration) { 2885 if (isLoggable) { 2886 Log.v(TAG, " Dropping sync operation: disallowed by settings/network."); 2887 } 2888 return false; 2889 } 2890 // Metered network. 2891 if (op.isNotAllowedOnMetered() && getConnectivityManager().isActiveNetworkMetered() 2892 && !ignoreSystemConfiguration) { 2893 if (isLoggable) { 2894 Log.v(TAG, " Dropping sync operation: not allowed on metered network."); 2895 } 2896 return false; 2897 } 2898 return true; 2899 } 2900 2901 private boolean dispatchSyncOperation(SyncOperation op) { 2902 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2903 Log.v(TAG, "dispatchSyncOperation: we are going to sync " + op); 2904 Log.v(TAG, "num active syncs: " + mActiveSyncContexts.size()); 2905 for (ActiveSyncContext syncContext : mActiveSyncContexts) { 2906 Log.v(TAG, syncContext.toString()); 2907 } 2908 } 2909 // Connect to the sync adapter. 2910 int targetUid; 2911 ComponentName targetComponent; 2912 final SyncStorageEngine.EndPoint info = op.target; 2913 if (info.target_provider) { 2914 SyncAdapterType syncAdapterType = 2915 SyncAdapterType.newKey(info.provider, info.account.type); 2916 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; 2917 syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, info.userId); 2918 if (syncAdapterInfo == null) { 2919 Log.d(TAG, "can't find a sync adapter for " + syncAdapterType 2920 + ", removing settings for it"); 2921 mSyncStorageEngine.removeAuthority(info); 2922 return false; 2923 } 2924 targetUid = syncAdapterInfo.uid; 2925 targetComponent = syncAdapterInfo.componentName; 2926 } else { 2927 // TODO: Store the uid of the service as part of the authority info in order to 2928 // avoid this call? 2929 try { 2930 targetUid = mContext.getPackageManager() 2931 .getServiceInfo(info.service, 0) 2932 .applicationInfo 2933 .uid; 2934 targetComponent = info.service; 2935 } catch(PackageManager.NameNotFoundException e) { 2936 Log.d(TAG, "Can't find a service for " + info.service 2937 + ", removing settings for it"); 2938 mSyncStorageEngine.removeAuthority(info); 2939 return false; 2940 } 2941 } 2942 ActiveSyncContext activeSyncContext = 2943 new ActiveSyncContext(op, insertStartSyncEvent(op), targetUid); 2944 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2945 Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext); 2946 } 2947 2948 activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext); 2949 mActiveSyncContexts.add(activeSyncContext); 2950 // Post message to cancel this sync if it runs for too long. 2951 if (!activeSyncContext.mSyncOperation.isExpedited() && 2952 !activeSyncContext.mSyncOperation.isManual() && 2953 !activeSyncContext.mSyncOperation.isIgnoreSettings()) { 2954 postSyncExpiryMessage(activeSyncContext); 2955 } 2956 2957 // Post message to begin monitoring this sync's progress. 2958 postMonitorSyncProgressMessage(activeSyncContext); 2959 2960 if (!activeSyncContext.bindToSyncAdapter(targetComponent, info.userId)) { 2961 Log.e(TAG, "Bind attempt failed - target: " + targetComponent); 2962 closeActiveSyncContext(activeSyncContext); 2963 return false; 2964 } 2965 2966 return true; 2967 } 2968 2969 private void runBoundToAdapter(final ActiveSyncContext activeSyncContext, 2970 IBinder syncAdapter) { 2971 final SyncOperation syncOperation = activeSyncContext.mSyncOperation; 2972 try { 2973 activeSyncContext.mIsLinkedToDeath = true; 2974 syncAdapter.linkToDeath(activeSyncContext, 0); 2975 2976 if (syncOperation.target.target_provider) { 2977 activeSyncContext.mSyncAdapter = ISyncAdapter.Stub.asInterface(syncAdapter); 2978 activeSyncContext.mSyncAdapter 2979 .startSync(activeSyncContext, syncOperation.target.provider, 2980 syncOperation.target.account, syncOperation.extras); 2981 } else if (syncOperation.target.target_service) { 2982 activeSyncContext.mSyncServiceAdapter = 2983 ISyncServiceAdapter.Stub.asInterface(syncAdapter); 2984 activeSyncContext.mSyncServiceAdapter 2985 .startSync(activeSyncContext, syncOperation.extras); 2986 } 2987 } catch (RemoteException remoteExc) { 2988 Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc); 2989 closeActiveSyncContext(activeSyncContext); 2990 increaseBackoffSetting(syncOperation); 2991 scheduleSyncOperation( 2992 new SyncOperation(syncOperation, 0L /* newRunTimeFromNow */)); 2993 } catch (RuntimeException exc) { 2994 closeActiveSyncContext(activeSyncContext); 2995 Log.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc); 2996 } 2997 } 2998 2999 /** 3000 * Cancel the sync for the provided target that matches the given bundle. 3001 * @param info Can have null fields to indicate all the active syncs for that field. 3002 * @param extras Can be null to indicate <strong>all</strong> syncs for the given endpoint. 3003 */ 3004 private void cancelActiveSyncH(SyncStorageEngine.EndPoint info, Bundle extras) { 3005 ArrayList<ActiveSyncContext> activeSyncs = 3006 new ArrayList<ActiveSyncContext>(mActiveSyncContexts); 3007 for (ActiveSyncContext activeSyncContext : activeSyncs) { 3008 if (activeSyncContext != null) { 3009 final SyncStorageEngine.EndPoint opInfo = 3010 activeSyncContext.mSyncOperation.target; 3011 if (!opInfo.matchesSpec(info)) { 3012 continue; 3013 } 3014 if (extras != null && 3015 !syncExtrasEquals(activeSyncContext.mSyncOperation.extras, 3016 extras, 3017 false /* no config settings */)) { 3018 continue; 3019 } 3020 runSyncFinishedOrCanceledH(null /* cancel => no result */, activeSyncContext); 3021 } 3022 } 3023 } 3024 3025 private void runSyncFinishedOrCanceledH(SyncResult syncResult, 3026 ActiveSyncContext activeSyncContext) { 3027 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 3028 3029 final SyncOperation syncOperation = activeSyncContext.mSyncOperation; 3030 final SyncStorageEngine.EndPoint info = syncOperation.target; 3031 3032 if (activeSyncContext.mIsLinkedToDeath) { 3033 if (info.target_provider) { 3034 activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0); 3035 } else { 3036 activeSyncContext.mSyncServiceAdapter.asBinder() 3037 .unlinkToDeath(activeSyncContext, 0); 3038 } 3039 activeSyncContext.mIsLinkedToDeath = false; 3040 } 3041 closeActiveSyncContext(activeSyncContext); 3042 final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime; 3043 String historyMessage; 3044 int downstreamActivity; 3045 int upstreamActivity; 3046 if (syncResult != null) { 3047 if (isLoggable) { 3048 Log.v(TAG, "runSyncFinishedOrCanceled [finished]: " 3049 + syncOperation + ", result " + syncResult); 3050 } 3051 3052 if (!syncResult.hasError()) { 3053 historyMessage = SyncStorageEngine.MESG_SUCCESS; 3054 // TODO: set these correctly when the SyncResult is extended to include it 3055 downstreamActivity = 0; 3056 upstreamActivity = 0; 3057 clearBackoffSetting(syncOperation); 3058 } else { 3059 Log.d(TAG, "failed sync operation " + syncOperation + ", " + syncResult); 3060 // the operation failed so increase the backoff time 3061 increaseBackoffSetting(syncOperation); 3062 // reschedule the sync if so indicated by the syncResult 3063 maybeRescheduleSync(syncResult, syncOperation); 3064 historyMessage = ContentResolver.syncErrorToString( 3065 syncResultToErrorNumber(syncResult)); 3066 // TODO: set these correctly when the SyncResult is extended to include it 3067 downstreamActivity = 0; 3068 upstreamActivity = 0; 3069 } 3070 setDelayUntilTime(syncOperation, syncResult.delayUntil); 3071 } else { 3072 if (isLoggable) { 3073 Log.v(TAG, "runSyncFinishedOrCanceled [canceled]: " + syncOperation); 3074 } 3075 if (activeSyncContext.mSyncAdapter != null) { 3076 try { 3077 activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext); 3078 } catch (RemoteException e) { 3079 // we don't need to retry this in this case 3080 } 3081 } else if (activeSyncContext.mSyncServiceAdapter != null) { 3082 try { 3083 activeSyncContext.mSyncServiceAdapter.cancelSync(activeSyncContext); 3084 } catch (RemoteException e) { 3085 // we don't need to retry this in this case 3086 } 3087 } 3088 historyMessage = SyncStorageEngine.MESG_CANCELED; 3089 downstreamActivity = 0; 3090 upstreamActivity = 0; 3091 } 3092 3093 stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage, 3094 upstreamActivity, downstreamActivity, elapsedTime); 3095 // Check for full-resync and schedule it after closing off the last sync. 3096 if (info.target_provider) { 3097 if (syncResult != null && syncResult.tooManyDeletions) { 3098 installHandleTooManyDeletesNotification(info.account, 3099 info.provider, syncResult.stats.numDeletes, 3100 info.userId); 3101 } else { 3102 mNotificationMgr.cancelAsUser(null, 3103 info.account.hashCode() ^ info.provider.hashCode(), 3104 new UserHandle(info.userId)); 3105 } 3106 if (syncResult != null && syncResult.fullSyncRequested) { 3107 scheduleSyncOperation( 3108 new SyncOperation(info.account, info.userId, 3109 syncOperation.reason, 3110 syncOperation.syncSource, info.provider, new Bundle(), 3111 0 /* delay */, 0 /* flex */, 3112 syncOperation.backoff, syncOperation.delayUntil, 3113 syncOperation.allowParallelSyncs)); 3114 } 3115 } else { 3116 if (syncResult != null && syncResult.fullSyncRequested) { 3117 scheduleSyncOperation( 3118 new SyncOperation(info.service, info.userId, 3119 syncOperation.reason, 3120 syncOperation.syncSource, new Bundle(), 3121 0 /* delay */, 0 /* flex */, 3122 syncOperation.backoff, syncOperation.delayUntil)); 3123 } 3124 } 3125 // no need to schedule an alarm, as that will be done by our caller. 3126 } 3127 3128 private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) { 3129 activeSyncContext.close(); 3130 mActiveSyncContexts.remove(activeSyncContext); 3131 mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo, 3132 activeSyncContext.mSyncOperation.target.userId); 3133 3134 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3135 Log.v(TAG, "removing all MESSAGE_MONITOR_SYNC & MESSAGE_SYNC_EXPIRED for " 3136 + activeSyncContext.toString()); 3137 } 3138 mSyncHandler.removeMessages(SyncHandler.MESSAGE_SYNC_EXPIRED, activeSyncContext); 3139 mSyncHandler.removeMessages(SyncHandler.MESSAGE_MONITOR_SYNC, activeSyncContext); 3140 } 3141 3142 /** 3143 * Convert the error-containing SyncResult into the Sync.History error number. Since 3144 * the SyncResult may indicate multiple errors at once, this method just returns the 3145 * most "serious" error. 3146 * @param syncResult the SyncResult from which to read 3147 * @return the most "serious" error set in the SyncResult 3148 * @throws IllegalStateException if the SyncResult does not indicate any errors. 3149 * If SyncResult.error() is true then it is safe to call this. 3150 */ 3151 private int syncResultToErrorNumber(SyncResult syncResult) { 3152 if (syncResult.syncAlreadyInProgress) 3153 return ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS; 3154 if (syncResult.stats.numAuthExceptions > 0) 3155 return ContentResolver.SYNC_ERROR_AUTHENTICATION; 3156 if (syncResult.stats.numIoExceptions > 0) 3157 return ContentResolver.SYNC_ERROR_IO; 3158 if (syncResult.stats.numParseExceptions > 0) 3159 return ContentResolver.SYNC_ERROR_PARSE; 3160 if (syncResult.stats.numConflictDetectedExceptions > 0) 3161 return ContentResolver.SYNC_ERROR_CONFLICT; 3162 if (syncResult.tooManyDeletions) 3163 return ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS; 3164 if (syncResult.tooManyRetries) 3165 return ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES; 3166 if (syncResult.databaseError) 3167 return ContentResolver.SYNC_ERROR_INTERNAL; 3168 throw new IllegalStateException("we are not in an error state, " + syncResult); 3169 } 3170 3171 private void manageSyncNotificationLocked() { 3172 boolean shouldCancel; 3173 boolean shouldInstall; 3174 3175 if (mActiveSyncContexts.isEmpty()) { 3176 mSyncNotificationInfo.startTime = null; 3177 3178 // we aren't syncing. if the notification is active then remember that we need 3179 // to cancel it and then clear out the info 3180 shouldCancel = mSyncNotificationInfo.isActive; 3181 shouldInstall = false; 3182 } else { 3183 // we are syncing 3184 final long now = SystemClock.elapsedRealtime(); 3185 if (mSyncNotificationInfo.startTime == null) { 3186 mSyncNotificationInfo.startTime = now; 3187 } 3188 3189 // there are three cases: 3190 // - the notification is up: do nothing 3191 // - the notification is not up but it isn't time yet: don't install 3192 // - the notification is not up and it is time: need to install 3193 3194 if (mSyncNotificationInfo.isActive) { 3195 shouldInstall = shouldCancel = false; 3196 } else { 3197 // it isn't currently up, so there is nothing to cancel 3198 shouldCancel = false; 3199 3200 final boolean timeToShowNotification = 3201 now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY; 3202 if (timeToShowNotification) { 3203 shouldInstall = true; 3204 } else { 3205 // show the notification immediately if this is a manual sync 3206 shouldInstall = false; 3207 for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) { 3208 final boolean manualSync = activeSyncContext.mSyncOperation.extras 3209 .getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false); 3210 if (manualSync) { 3211 shouldInstall = true; 3212 break; 3213 } 3214 } 3215 } 3216 } 3217 } 3218 3219 if (shouldCancel && !shouldInstall) { 3220 mNeedSyncActiveNotification = false; 3221 sendSyncStateIntent(); 3222 mSyncNotificationInfo.isActive = false; 3223 } 3224 3225 if (shouldInstall) { 3226 mNeedSyncActiveNotification = true; 3227 sendSyncStateIntent(); 3228 mSyncNotificationInfo.isActive = true; 3229 } 3230 } 3231 3232 private void manageSyncAlarmLocked(long nextPeriodicEventElapsedTime, 3233 long nextPendingEventElapsedTime) { 3234 // in each of these cases the sync loop will be kicked, which will cause this 3235 // method to be called again 3236 if (!mDataConnectionIsConnected) return; 3237 if (mStorageIsLow) return; 3238 if (mDeviceIsIdle) return; 3239 3240 // When the status bar notification should be raised 3241 final long notificationTime = 3242 (!mSyncHandler.mSyncNotificationInfo.isActive 3243 && mSyncHandler.mSyncNotificationInfo.startTime != null) 3244 ? mSyncHandler.mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY 3245 : Long.MAX_VALUE; 3246 3247 // When we should consider canceling an active sync 3248 long earliestTimeoutTime = Long.MAX_VALUE; 3249 for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) { 3250 final long currentSyncTimeoutTime = 3251 currentSyncContext.mTimeoutStartTime + MAX_TIME_PER_SYNC; 3252 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3253 Log.v(TAG, "manageSyncAlarm: active sync, mTimeoutStartTime + MAX is " 3254 + currentSyncTimeoutTime); 3255 } 3256 if (earliestTimeoutTime > currentSyncTimeoutTime) { 3257 earliestTimeoutTime = currentSyncTimeoutTime; 3258 } 3259 } 3260 3261 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3262 Log.v(TAG, "manageSyncAlarm: notificationTime is " + notificationTime); 3263 } 3264 3265 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3266 Log.v(TAG, "manageSyncAlarm: earliestTimeoutTime is " + earliestTimeoutTime); 3267 } 3268 3269 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3270 Log.v(TAG, "manageSyncAlarm: nextPeriodicEventElapsedTime is " 3271 + nextPeriodicEventElapsedTime); 3272 } 3273 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3274 Log.v(TAG, "manageSyncAlarm: nextPendingEventElapsedTime is " 3275 + nextPendingEventElapsedTime); 3276 } 3277 3278 long alarmTime = Math.min(notificationTime, earliestTimeoutTime); 3279 alarmTime = Math.min(alarmTime, nextPeriodicEventElapsedTime); 3280 alarmTime = Math.min(alarmTime, nextPendingEventElapsedTime); 3281 3282 // Bound the alarm time. 3283 final long now = SystemClock.elapsedRealtime(); 3284 if (alarmTime < now + SYNC_ALARM_TIMEOUT_MIN) { 3285 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3286 Log.v(TAG, "manageSyncAlarm: the alarmTime is too small, " 3287 + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN)); 3288 } 3289 alarmTime = now + SYNC_ALARM_TIMEOUT_MIN; 3290 } else if (alarmTime > now + SYNC_ALARM_TIMEOUT_MAX) { 3291 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3292 Log.v(TAG, "manageSyncAlarm: the alarmTime is too large, " 3293 + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN)); 3294 } 3295 alarmTime = now + SYNC_ALARM_TIMEOUT_MAX; 3296 } 3297 3298 // determine if we need to set or cancel the alarm 3299 boolean shouldSet = false; 3300 boolean shouldCancel = false; 3301 final boolean alarmIsActive = (mAlarmScheduleTime != null) && (now < mAlarmScheduleTime); 3302 final boolean needAlarm = alarmTime != Long.MAX_VALUE; 3303 if (needAlarm) { 3304 // Need the alarm if 3305 // - it's currently not set 3306 // - if the alarm is set in the past. 3307 if (!alarmIsActive || alarmTime < mAlarmScheduleTime) { 3308 shouldSet = true; 3309 } 3310 } else { 3311 shouldCancel = alarmIsActive; 3312 } 3313 3314 // Set or cancel the alarm as directed. 3315 ensureAlarmService(); 3316 if (shouldSet) { 3317 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3318 Log.v(TAG, "requesting that the alarm manager wake us up at elapsed time " 3319 + alarmTime + ", now is " + now + ", " + ((alarmTime - now) / 1000) 3320 + " secs from now"); 3321 } 3322 mAlarmScheduleTime = alarmTime; 3323 mAlarmService.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime, 3324 mSyncAlarmIntent); 3325 } else if (shouldCancel) { 3326 mAlarmScheduleTime = null; 3327 mAlarmService.cancel(mSyncAlarmIntent); 3328 } 3329 } 3330 3331 private void sendSyncStateIntent() { 3332 Intent syncStateIntent = new Intent(Intent.ACTION_SYNC_STATE_CHANGED); 3333 syncStateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 3334 syncStateIntent.putExtra("active", mNeedSyncActiveNotification); 3335 syncStateIntent.putExtra("failing", false); 3336 mContext.sendBroadcastAsUser(syncStateIntent, UserHandle.OWNER); 3337 } 3338 3339 private void installHandleTooManyDeletesNotification(Account account, String authority, 3340 long numDeletes, int userId) { 3341 if (mNotificationMgr == null) return; 3342 3343 final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider( 3344 authority, 0 /* flags */); 3345 if (providerInfo == null) { 3346 return; 3347 } 3348 CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager()); 3349 3350 Intent clickIntent = new Intent(mContext, SyncActivityTooManyDeletes.class); 3351 clickIntent.putExtra("account", account); 3352 clickIntent.putExtra("authority", authority); 3353 clickIntent.putExtra("provider", authorityName.toString()); 3354 clickIntent.putExtra("numDeletes", numDeletes); 3355 3356 if (!isActivityAvailable(clickIntent)) { 3357 Log.w(TAG, "No activity found to handle too many deletes."); 3358 return; 3359 } 3360 3361 UserHandle user = new UserHandle(userId); 3362 final PendingIntent pendingIntent = PendingIntent 3363 .getActivityAsUser(mContext, 0, clickIntent, 3364 PendingIntent.FLAG_CANCEL_CURRENT, null, user); 3365 3366 CharSequence tooManyDeletesDescFormat = mContext.getResources().getText( 3367 R.string.contentServiceTooManyDeletesNotificationDesc); 3368 3369 Context contextForUser = getContextForUser(user); 3370 Notification notification = new Notification.Builder(contextForUser) 3371 .setSmallIcon(R.drawable.stat_notify_sync_error) 3372 .setTicker(mContext.getString(R.string.contentServiceSync)) 3373 .setWhen(System.currentTimeMillis()) 3374 .setColor(contextForUser.getColor( 3375 com.android.internal.R.color.system_notification_accent_color)) 3376 .setContentTitle(contextForUser.getString( 3377 R.string.contentServiceSyncNotificationTitle)) 3378 .setContentText( 3379 String.format(tooManyDeletesDescFormat.toString(), authorityName)) 3380 .setContentIntent(pendingIntent) 3381 .build(); 3382 notification.flags |= Notification.FLAG_ONGOING_EVENT; 3383 mNotificationMgr.notifyAsUser(null, account.hashCode() ^ authority.hashCode(), 3384 notification, user); 3385 } 3386 3387 /** 3388 * Checks whether an activity exists on the system image for the given intent. 3389 * 3390 * @param intent The intent for an activity. 3391 * @return Whether or not an activity exists. 3392 */ 3393 private boolean isActivityAvailable(Intent intent) { 3394 PackageManager pm = mContext.getPackageManager(); 3395 List<ResolveInfo> list = pm.queryIntentActivities(intent, 0); 3396 int listSize = list.size(); 3397 for (int i = 0; i < listSize; i++) { 3398 ResolveInfo resolveInfo = list.get(i); 3399 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 3400 != 0) { 3401 return true; 3402 } 3403 } 3404 3405 return false; 3406 } 3407 3408 public long insertStartSyncEvent(SyncOperation syncOperation) { 3409 final long now = System.currentTimeMillis(); 3410 EventLog.writeEvent(2720, 3411 syncOperation.toEventLog(SyncStorageEngine.EVENT_START)); 3412 return mSyncStorageEngine.insertStartSyncEvent(syncOperation, now); 3413 } 3414 3415 public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, 3416 int upstreamActivity, int downstreamActivity, long elapsedTime) { 3417 EventLog.writeEvent(2720, 3418 syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP)); 3419 mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime, 3420 resultMessage, downstreamActivity, upstreamActivity); 3421 } 3422 } 3423 3424 String getPackageName(EndPoint endpoint) { 3425 if (endpoint.target_service) { 3426 return endpoint.service.getPackageName(); 3427 } else { 3428 SyncAdapterType syncAdapterType = 3429 SyncAdapterType.newKey(endpoint.provider, endpoint.account.type); 3430 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; 3431 syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, endpoint.userId); 3432 if (syncAdapterInfo == null) { 3433 return null; 3434 } 3435 return syncAdapterInfo.componentName.getPackageName(); 3436 } 3437 } 3438 3439 private boolean isSyncStillActiveH(ActiveSyncContext activeSyncContext) { 3440 for (ActiveSyncContext sync : mActiveSyncContexts) { 3441 if (sync == activeSyncContext) { 3442 return true; 3443 } 3444 } 3445 return false; 3446 } 3447 3448 /** 3449 * Sync extra comparison function. 3450 * @param b1 bundle to compare 3451 * @param b2 other bundle to compare 3452 * @param includeSyncSettings if false, ignore system settings in bundle. 3453 */ 3454 public static boolean syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSyncSettings) { 3455 if (b1 == b2) { 3456 return true; 3457 } 3458 // Exit early if we can. 3459 if (includeSyncSettings && b1.size() != b2.size()) { 3460 return false; 3461 } 3462 Bundle bigger = b1.size() > b2.size() ? b1 : b2; 3463 Bundle smaller = b1.size() > b2.size() ? b2 : b1; 3464 for (String key : bigger.keySet()) { 3465 if (!includeSyncSettings && isSyncSetting(key)) { 3466 continue; 3467 } 3468 if (!smaller.containsKey(key)) { 3469 return false; 3470 } 3471 if (!bigger.get(key).equals(smaller.get(key))) { 3472 return false; 3473 } 3474 } 3475 return true; 3476 } 3477 3478 /** 3479 * TODO: Get rid of this when we separate sync settings extras from dev specified extras. 3480 * @return true if the provided key is used by the SyncManager in scheduling the sync. 3481 */ 3482 private static boolean isSyncSetting(String key) { 3483 if (key.equals(ContentResolver.SYNC_EXTRAS_EXPEDITED)) { 3484 return true; 3485 } 3486 if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS)) { 3487 return true; 3488 } 3489 if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF)) { 3490 return true; 3491 } 3492 if (key.equals(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY)) { 3493 return true; 3494 } 3495 if (key.equals(ContentResolver.SYNC_EXTRAS_MANUAL)) { 3496 return true; 3497 } 3498 if (key.equals(ContentResolver.SYNC_EXTRAS_UPLOAD)) { 3499 return true; 3500 } 3501 if (key.equals(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS)) { 3502 return true; 3503 } 3504 if (key.equals(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS)) { 3505 return true; 3506 } 3507 if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD)) { 3508 return true; 3509 } 3510 if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD)) { 3511 return true; 3512 } 3513 if (key.equals(ContentResolver.SYNC_EXTRAS_PRIORITY)) { 3514 return true; 3515 } 3516 if (key.equals(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED)) { 3517 return true; 3518 } 3519 if (key.equals(ContentResolver.SYNC_EXTRAS_INITIALIZE)) { 3520 return true; 3521 } 3522 return false; 3523 } 3524 3525 static class PrintTable { 3526 private ArrayList<Object[]> mTable = Lists.newArrayList(); 3527 private final int mCols; 3528 3529 PrintTable(int cols) { 3530 mCols = cols; 3531 } 3532 3533 void set(int row, int col, Object... values) { 3534 if (col + values.length > mCols) { 3535 throw new IndexOutOfBoundsException("Table only has " + mCols + 3536 " columns. can't set " + values.length + " at column " + col); 3537 } 3538 for (int i = mTable.size(); i <= row; i++) { 3539 final Object[] list = new Object[mCols]; 3540 mTable.add(list); 3541 for (int j = 0; j < mCols; j++) { 3542 list[j] = ""; 3543 } 3544 } 3545 System.arraycopy(values, 0, mTable.get(row), col, values.length); 3546 } 3547 3548 void writeTo(PrintWriter out) { 3549 final String[] formats = new String[mCols]; 3550 int totalLength = 0; 3551 for (int col = 0; col < mCols; ++col) { 3552 int maxLength = 0; 3553 for (Object[] row : mTable) { 3554 final int length = row[col].toString().length(); 3555 if (length > maxLength) { 3556 maxLength = length; 3557 } 3558 } 3559 totalLength += maxLength; 3560 formats[col] = String.format("%%-%ds", maxLength); 3561 } 3562 formats[mCols - 1] = "%s"; 3563 printRow(out, formats, mTable.get(0)); 3564 totalLength += (mCols - 1) * 2; 3565 for (int i = 0; i < totalLength; ++i) { 3566 out.print("-"); 3567 } 3568 out.println(); 3569 for (int i = 1, mTableSize = mTable.size(); i < mTableSize; i++) { 3570 Object[] row = mTable.get(i); 3571 printRow(out, formats, row); 3572 } 3573 } 3574 3575 private void printRow(PrintWriter out, String[] formats, Object[] row) { 3576 for (int j = 0, rowLength = row.length; j < rowLength; j++) { 3577 out.printf(String.format(formats[j], row[j].toString())); 3578 out.print(" "); 3579 } 3580 out.println(); 3581 } 3582 3583 public int getNumRows() { 3584 return mTable.size(); 3585 } 3586 } 3587 3588 private Context getContextForUser(UserHandle user) { 3589 try { 3590 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user); 3591 } catch (NameNotFoundException e) { 3592 // Default to mContext, not finding the package system is running as is unlikely. 3593 return mContext; 3594 } 3595 } 3596} 3597