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