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