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