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 17 package com.android.server.content; 18 19 import android.accounts.Account; 20 import android.accounts.AccountAndUser; 21 import android.accounts.AccountManager; 22 import android.app.ActivityManager; 23 import android.app.ActivityManagerNative; 24 import android.app.AppGlobals; 25 import android.app.Notification; 26 import android.app.NotificationManager; 27 import android.app.PendingIntent; 28 import android.app.job.JobInfo; 29 import android.app.job.JobScheduler; 30 import android.content.BroadcastReceiver; 31 import android.content.ComponentName; 32 import android.content.ContentResolver; 33 import android.content.Context; 34 import android.content.ISyncAdapter; 35 import android.content.ISyncContext; 36 import android.content.Intent; 37 import android.content.IntentFilter; 38 import android.content.PeriodicSync; 39 import android.content.ServiceConnection; 40 import android.content.SyncActivityTooManyDeletes; 41 import android.content.SyncAdapterType; 42 import android.content.SyncAdaptersCache; 43 import android.content.SyncInfo; 44 import android.content.SyncResult; 45 import android.content.SyncStatusInfo; 46 import android.content.pm.ApplicationInfo; 47 import android.content.pm.PackageInfo; 48 import android.content.pm.PackageManager; 49 import android.content.pm.PackageManager.NameNotFoundException; 50 import android.content.pm.ProviderInfo; 51 import android.content.pm.RegisteredServicesCache; 52 import android.content.pm.RegisteredServicesCacheListener; 53 import android.content.pm.ResolveInfo; 54 import android.content.pm.UserInfo; 55 import android.database.ContentObserver; 56 import android.net.ConnectivityManager; 57 import android.net.NetworkInfo; 58 import android.net.TrafficStats; 59 import android.os.BatteryStats; 60 import android.os.Bundle; 61 import android.os.Handler; 62 import android.os.IBinder; 63 import android.os.Looper; 64 import android.os.Message; 65 import android.os.Messenger; 66 import android.os.PowerManager; 67 import android.os.RemoteException; 68 import android.os.ServiceManager; 69 import android.os.SystemClock; 70 import android.os.SystemProperties; 71 import android.os.UserHandle; 72 import android.os.UserManager; 73 import android.os.WorkSource; 74 import android.provider.Settings; 75 import android.text.format.DateUtils; 76 import android.text.format.Time; 77 import android.util.EventLog; 78 import android.util.Log; 79 import android.util.Pair; 80 import android.util.Slog; 81 82 import com.android.server.LocalServices; 83 import com.android.server.job.JobSchedulerInternal; 84 import com.google.android.collect.Lists; 85 import com.google.android.collect.Maps; 86 87 import com.android.internal.R; 88 import com.android.internal.app.IBatteryStats; 89 import com.android.internal.os.BackgroundThread; 90 import com.android.internal.util.IndentingPrintWriter; 91 import com.android.server.accounts.AccountManagerService; 92 import com.android.server.backup.AccountSyncSettingsBackupHelper; 93 import com.android.server.content.SyncStorageEngine.AuthorityInfo; 94 import com.android.server.content.SyncStorageEngine.EndPoint; 95 import com.android.server.content.SyncStorageEngine.OnSyncRequestListener; 96 97 import java.io.FileDescriptor; 98 import java.io.PrintWriter; 99 import java.util.ArrayList; 100 import java.util.Arrays; 101 import java.util.Collection; 102 import java.util.Collections; 103 import java.util.Comparator; 104 import java.util.HashMap; 105 import java.util.HashSet; 106 import java.util.List; 107 import java.util.Map; 108 import java.util.Objects; 109 import java.util.Random; 110 import 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 */ 133 public 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 isJobIdInUseLockedH(int jobId, List<JobInfo> pendingJobs)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 getUnusedJobIdH()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 getAllPendingSyncs()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 getAllUsers()313 private List<UserInfo> getAllUsers() { 314 return mUserManager.getUsers(); 315 } 316 containsAccountAndUser(AccountAndUser[] accounts, Account account, int userId)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. */ updateRunningAccounts(EndPoint target)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 doDatabaseCleanup()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 clearAllBackoffs()369 private void clearAllBackoffs() { 370 mSyncStorageEngine.clearAllBackoffsLocked(); 371 rescheduleSyncs(EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL); 372 } 373 readDataConnectionState()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_STOPPED.equals(action)) { 400 onUserStopped(userId); 401 } 402 } 403 }; 404 405 private final SyncHandler mSyncHandler; 406 407 private volatile boolean mBootCompleted = false; 408 private volatile boolean mJobServiceReady = false; 409 getConnectivityManager()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 */ cleanupJobs()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 verifyJobScheduler()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 getJobScheduler()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 */ SyncManager(Context context, boolean factoryTest)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_STOPPED); 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 isDeviceProvisioned()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 */ jitterize(long minValue, long maxValue)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 getSyncStorageEngine()654 public SyncStorageEngine getSyncStorageEngine() { 655 return mSyncStorageEngine; 656 } 657 getIsSyncable(Account account, int userId, String providerName)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 setAuthorityPendingState(EndPoint info)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 */ scheduleSync(Account requestedAccount, int userId, int reason, String requestedAuthority, Bundle extras, long beforeRuntimeMillis, long runtimeMillis, boolean onlyThoseWithUnkownSyncableState)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 removeSyncsForAuthority(EndPoint info)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 */ removePeriodicSync(EndPoint target, Bundle extras)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 */ updateOrAddPeriodicSync(EndPoint target, long pollFrequency, long flex, Bundle extras)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 */ getPeriodicSyncs(EndPoint target)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 */ scheduleLocalSync(Account account, int userId, int reason, String authority)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 getSyncAdapterTypes(int userId)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 getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId)980 public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) { 981 return mSyncAdapters.getSyncAdapterPackagesForAuthority(authority, userId); 982 } 983 sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext, SyncResult syncResult)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 sendCancelSyncsMessage(final SyncStorageEngine.EndPoint info, Bundle extras)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 */ postMonitorSyncProgressMessage(ActiveSyncContext activeSyncContext)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 postScheduleSyncMessage(SyncOperation syncOperation)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 */ getTotalBytesTransferredByUid(int uid)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 SyncFinishedOrCancelledMessagePayload(ActiveSyncContext syncContext, SyncResult syncResult)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 UpdatePeriodicSyncMessagePayload(EndPoint target, long pollFrequency, long flex, Bundle extras)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 clearBackoffSetting(EndPoint target)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 increaseBackoffSetting(EndPoint target)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 */ rescheduleSyncs(EndPoint target)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 setDelayUntilTime(EndPoint target, long delayUntilSeconds)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 isAdapterDelayed(EndPoint target)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 */ cancelActiveSync(SyncStorageEngine.EndPoint info, Bundle extras)1175 public void cancelActiveSync(SyncStorageEngine.EndPoint info, Bundle extras) { 1176 sendCancelSyncsMessage(info, extras); 1177 } 1178 1179 /** 1180 * Schedule a sync operation with JobScheduler. 1181 */ scheduleSyncOperationH(SyncOperation syncOperation)1182 private void scheduleSyncOperationH(SyncOperation syncOperation) { 1183 scheduleSyncOperationH(syncOperation, 0L); 1184 } 1185 scheduleSyncOperationH(SyncOperation syncOperation, long minDelay)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 */ clearScheduledSyncOperations(SyncStorageEngine.EndPoint info)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 */ cancelScheduledSyncOperation(SyncStorageEngine.EndPoint info, Bundle extras)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 maybeRescheduleSync(SyncResult syncResult, SyncOperation operation)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 && !syncResult.syncAlreadyInProgress) { 1360 // syncAlreadyInProgress flag is set by AbstractThreadedSyncAdapter. The sync adapter 1361 // has no way of knowing that a sync error occured. So we DO retry if the error is 1362 // syncAlreadyInProgress. 1363 if (isLoggable) { 1364 Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified " 1365 + operation); 1366 } 1367 } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false) 1368 && !syncResult.syncAlreadyInProgress) { 1369 // If this was an upward sync then schedule a two-way sync immediately. 1370 operation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD); 1371 if (isLoggable) { 1372 Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync " 1373 + "encountered an error: " + operation); 1374 } 1375 scheduleSyncOperationH(operation); 1376 } else if (syncResult.tooManyRetries) { 1377 // If this sync aborted because the internal sync loop retried too many times then 1378 // don't reschedule. Otherwise we risk getting into a retry loop. 1379 if (isLoggable) { 1380 Log.d(TAG, "not retrying sync operation because it retried too many times: " 1381 + operation); 1382 } 1383 } else if (syncResult.madeSomeProgress()) { 1384 // If the operation succeeded to some extent then retry immediately. 1385 if (isLoggable) { 1386 Log.d(TAG, "retrying sync operation because even though it had an error " 1387 + "it achieved some success"); 1388 } 1389 scheduleSyncOperationH(operation); 1390 } else if (syncResult.syncAlreadyInProgress) { 1391 if (isLoggable) { 1392 Log.d(TAG, "retrying sync operation that failed because there was already a " 1393 + "sync in progress: " + operation); 1394 } 1395 scheduleSyncOperationH(operation, DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000); 1396 } else if (syncResult.hasSoftError()) { 1397 // If this was a two-way sync then retry soft errors with an exponential backoff. 1398 if (isLoggable) { 1399 Log.d(TAG, "retrying sync operation because it encountered a soft error: " 1400 + operation); 1401 } 1402 scheduleSyncOperationH(operation); 1403 } else { 1404 // Otherwise do not reschedule. 1405 Log.d(TAG, "not retrying sync operation because the error is a hard error: " 1406 + operation); 1407 } 1408 } 1409 onUserUnlocked(int userId)1410 private void onUserUnlocked(int userId) { 1411 // Make sure that accounts we're about to use are valid. 1412 AccountManagerService.getSingleton().validateAccounts(userId); 1413 1414 mSyncAdapters.invalidateCache(userId); 1415 1416 EndPoint target = new EndPoint(null, null, userId); 1417 updateRunningAccounts(target); 1418 1419 // Schedule sync for any accounts under started user. 1420 final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId, 1421 mContext.getOpPackageName()); 1422 for (Account account : accounts) { 1423 scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null, 1424 0 /* no delay */, 0 /* No flexMillis */, 1425 true /* onlyThoseWithUnknownSyncableState */); 1426 } 1427 } 1428 onUserStopped(int userId)1429 private void onUserStopped(int userId) { 1430 updateRunningAccounts(null /* Don't sync any target */); 1431 1432 cancelActiveSync( 1433 new SyncStorageEngine.EndPoint( 1434 null /* any account */, 1435 null /* any authority */, 1436 userId), 1437 null /* any sync. */ 1438 ); 1439 } 1440 onUserRemoved(int userId)1441 private void onUserRemoved(int userId) { 1442 updateRunningAccounts(null /* Don't sync any target */); 1443 1444 // Clean up the storage engine database 1445 mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId); 1446 List<SyncOperation> ops = getAllPendingSyncs(); 1447 for (SyncOperation op: ops) { 1448 if (op.target.userId == userId) { 1449 getJobScheduler().cancel(op.jobId); 1450 } 1451 } 1452 } 1453 1454 /** 1455 * @hide 1456 */ 1457 class ActiveSyncContext extends ISyncContext.Stub 1458 implements ServiceConnection, IBinder.DeathRecipient { 1459 final SyncOperation mSyncOperation; 1460 final long mHistoryRowId; 1461 ISyncAdapter mSyncAdapter; 1462 final long mStartTime; 1463 long mTimeoutStartTime; 1464 boolean mBound; 1465 final PowerManager.WakeLock mSyncWakeLock; 1466 final int mSyncAdapterUid; 1467 SyncInfo mSyncInfo; 1468 boolean mIsLinkedToDeath = false; 1469 String mEventName; 1470 1471 /** Total bytes transferred, counted at {@link #mLastPolledTimeElapsed} */ 1472 long mBytesTransferredAtLastPoll; 1473 /** 1474 * Last point in {@link SystemClock#elapsedRealtime()} at which we checked the # of bytes 1475 * transferred to/fro by this adapter. 1476 */ 1477 long mLastPolledTimeElapsed; 1478 1479 /** 1480 * Create an ActiveSyncContext for an impending sync and grab the wakelock for that 1481 * sync adapter. Since this grabs the wakelock you need to be sure to call 1482 * close() when you are done with this ActiveSyncContext, whether the sync succeeded 1483 * or not. 1484 * @param syncOperation the SyncOperation we are about to sync 1485 * @param historyRowId the row in which to record the history info for this sync 1486 * @param syncAdapterUid the UID of the application that contains the sync adapter 1487 * for this sync. This is used to attribute the wakelock hold to that application. 1488 */ ActiveSyncContext(SyncOperation syncOperation, long historyRowId, int syncAdapterUid)1489 public ActiveSyncContext(SyncOperation syncOperation, long historyRowId, 1490 int syncAdapterUid) { 1491 super(); 1492 mSyncAdapterUid = syncAdapterUid; 1493 mSyncOperation = syncOperation; 1494 mHistoryRowId = historyRowId; 1495 mSyncAdapter = null; 1496 mStartTime = SystemClock.elapsedRealtime(); 1497 mTimeoutStartTime = mStartTime; 1498 mSyncWakeLock = mSyncHandler.getSyncWakeLock(mSyncOperation); 1499 mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterUid)); 1500 mSyncWakeLock.acquire(); 1501 } 1502 sendHeartbeat()1503 public void sendHeartbeat() { 1504 // Heartbeats are no longer used. 1505 } 1506 onFinished(SyncResult result)1507 public void onFinished(SyncResult result) { 1508 if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "onFinished: " + this); 1509 // Include "this" in the message so that the handler can ignore it if this 1510 // ActiveSyncContext is no longer the mActiveSyncContext at message handling 1511 // time. 1512 sendSyncFinishedOrCanceledMessage(this, result); 1513 } 1514 toString(StringBuilder sb)1515 public void toString(StringBuilder sb) { 1516 sb.append("startTime ").append(mStartTime) 1517 .append(", mTimeoutStartTime ").append(mTimeoutStartTime) 1518 .append(", mHistoryRowId ").append(mHistoryRowId) 1519 .append(", syncOperation ").append(mSyncOperation); 1520 } 1521 onServiceConnected(ComponentName name, IBinder service)1522 public void onServiceConnected(ComponentName name, IBinder service) { 1523 Message msg = mSyncHandler.obtainMessage(); 1524 msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED; 1525 msg.obj = new ServiceConnectionData(this, service); 1526 mSyncHandler.sendMessage(msg); 1527 } 1528 onServiceDisconnected(ComponentName name)1529 public void onServiceDisconnected(ComponentName name) { 1530 Message msg = mSyncHandler.obtainMessage(); 1531 msg.what = SyncHandler.MESSAGE_SERVICE_DISCONNECTED; 1532 msg.obj = new ServiceConnectionData(this, null); 1533 mSyncHandler.sendMessage(msg); 1534 } 1535 bindToSyncAdapter(ComponentName serviceComponent, int userId)1536 boolean bindToSyncAdapter(ComponentName serviceComponent, int userId) { 1537 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1538 Log.d(TAG, "bindToSyncAdapter: " + serviceComponent + ", connection " + this); 1539 } 1540 Intent intent = new Intent(); 1541 intent.setAction("android.content.SyncAdapter"); 1542 intent.setComponent(serviceComponent); 1543 intent.putExtra(Intent.EXTRA_CLIENT_LABEL, 1544 com.android.internal.R.string.sync_binding_label); 1545 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser( 1546 mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0, 1547 null, new UserHandle(userId))); 1548 mBound = true; 1549 final boolean bindResult = mContext.bindServiceAsUser(intent, this, 1550 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND 1551 | Context.BIND_ALLOW_OOM_MANAGEMENT, 1552 new UserHandle(mSyncOperation.target.userId)); 1553 if (!bindResult) { 1554 mBound = false; 1555 } else { 1556 try { 1557 mEventName = mSyncOperation.wakeLockName(); 1558 mBatteryStats.noteSyncStart(mEventName, mSyncAdapterUid); 1559 } catch (RemoteException e) { 1560 } 1561 } 1562 return bindResult; 1563 } 1564 1565 /** 1566 * Performs the required cleanup, which is the releasing of the wakelock and 1567 * unbinding from the sync adapter (if actually bound). 1568 */ close()1569 protected void close() { 1570 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1571 Log.d(TAG, "unBindFromSyncAdapter: connection " + this); 1572 } 1573 if (mBound) { 1574 mBound = false; 1575 mContext.unbindService(this); 1576 try { 1577 mBatteryStats.noteSyncFinish(mEventName, mSyncAdapterUid); 1578 } catch (RemoteException e) { 1579 } 1580 } 1581 mSyncWakeLock.release(); 1582 mSyncWakeLock.setWorkSource(null); 1583 } 1584 toString()1585 public String toString() { 1586 StringBuilder sb = new StringBuilder(); 1587 toString(sb); 1588 return sb.toString(); 1589 } 1590 1591 @Override binderDied()1592 public void binderDied() { 1593 sendSyncFinishedOrCanceledMessage(this, null); 1594 } 1595 } 1596 dump(FileDescriptor fd, PrintWriter pw)1597 protected void dump(FileDescriptor fd, PrintWriter pw) { 1598 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 1599 dumpPendingSyncs(pw); 1600 dumpPeriodicSyncs(pw); 1601 dumpSyncState(ipw); 1602 dumpSyncHistory(ipw); 1603 dumpSyncAdapters(ipw); 1604 } 1605 formatTime(long time)1606 static String formatTime(long time) { 1607 Time tobj = new Time(); 1608 tobj.set(time); 1609 return tobj.format("%Y-%m-%d %H:%M:%S"); 1610 } 1611 dumpPendingSyncs(PrintWriter pw)1612 protected void dumpPendingSyncs(PrintWriter pw) { 1613 pw.println("Pending Syncs:"); 1614 List<SyncOperation> pendingSyncs = getAllPendingSyncs(); 1615 int count = 0; 1616 for (SyncOperation op: pendingSyncs) { 1617 if (!op.isPeriodic) { 1618 pw.println(op.dump(null, false)); 1619 count++; 1620 } 1621 } 1622 pw.println("Total: " + count); 1623 pw.println(); 1624 } 1625 dumpPeriodicSyncs(PrintWriter pw)1626 protected void dumpPeriodicSyncs(PrintWriter pw) { 1627 pw.println("Periodic Syncs:"); 1628 List<SyncOperation> pendingSyncs = getAllPendingSyncs(); 1629 int count = 0; 1630 for (SyncOperation op: pendingSyncs) { 1631 if (op.isPeriodic) { 1632 pw.println(op.dump(null, false)); 1633 count++; 1634 } 1635 } 1636 pw.println("Total: " + count); 1637 pw.println(); 1638 } 1639 dumpSyncState(PrintWriter pw)1640 protected void dumpSyncState(PrintWriter pw) { 1641 pw.print("data connected: "); pw.println(mDataConnectionIsConnected); 1642 pw.print("auto sync: "); 1643 List<UserInfo> users = getAllUsers(); 1644 if (users != null) { 1645 for (UserInfo user : users) { 1646 pw.print("u" + user.id + "=" 1647 + mSyncStorageEngine.getMasterSyncAutomatically(user.id) + " "); 1648 } 1649 pw.println(); 1650 } 1651 pw.print("memory low: "); pw.println(mStorageIsLow); 1652 pw.print("device idle: "); pw.println(mDeviceIsIdle); 1653 pw.print("reported active: "); pw.println(mReportedSyncActive); 1654 1655 final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts(); 1656 1657 pw.print("accounts: "); 1658 if (accounts != INITIAL_ACCOUNTS_ARRAY) { 1659 pw.println(accounts.length); 1660 } else { 1661 pw.println("not known yet"); 1662 } 1663 final long now = SystemClock.elapsedRealtime(); 1664 pw.print("now: "); pw.print(now); 1665 pw.println(" (" + formatTime(System.currentTimeMillis()) + ")"); 1666 pw.println(" (HH:MM:SS)"); 1667 pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now / 1000)); 1668 pw.println(" (HH:MM:SS)"); 1669 pw.print("time spent syncing: "); 1670 pw.print(DateUtils.formatElapsedTime( 1671 mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000)); 1672 pw.print(" (HH:MM:SS), sync "); 1673 pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not "); 1674 pw.println("in progress"); 1675 1676 pw.println(); 1677 pw.println("Active Syncs: " + mActiveSyncContexts.size()); 1678 final PackageManager pm = mContext.getPackageManager(); 1679 for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) { 1680 final long durationInSeconds = (now - activeSyncContext.mStartTime) / 1000; 1681 pw.print(" "); 1682 pw.print(DateUtils.formatElapsedTime(durationInSeconds)); 1683 pw.print(" - "); 1684 pw.print(activeSyncContext.mSyncOperation.dump(pm, false)); 1685 pw.println(); 1686 } 1687 1688 // Join the installed sync adapter with the accounts list and emit for everything. 1689 pw.println(); 1690 pw.println("Sync Status"); 1691 for (AccountAndUser account : accounts) { 1692 pw.printf("Account %s u%d %s\n", 1693 account.account.name, account.userId, account.account.type); 1694 1695 pw.println("======================================================================="); 1696 final PrintTable table = new PrintTable(12); 1697 table.set(0, 0, 1698 "Authority", // 0 1699 "Syncable", // 1 1700 "Enabled", // 2 1701 "Delay", // 3 1702 "Loc", // 4 1703 "Poll", // 5 1704 "Per", // 6 1705 "Serv", // 7 1706 "User", // 8 1707 "Tot", // 9 1708 "Time", // 10 1709 "Last Sync" // 11 1710 ); 1711 1712 final List<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> sorted = 1713 Lists.newArrayList(); 1714 sorted.addAll(mSyncAdapters.getAllServices(account.userId)); 1715 Collections.sort(sorted, 1716 new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() { 1717 @Override 1718 public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs, 1719 RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) { 1720 return lhs.type.authority.compareTo(rhs.type.authority); 1721 } 1722 }); 1723 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : sorted) { 1724 if (!syncAdapterType.type.accountType.equals(account.account.type)) { 1725 continue; 1726 } 1727 int row = table.getNumRows(); 1728 Pair<AuthorityInfo, SyncStatusInfo> syncAuthoritySyncStatus = 1729 mSyncStorageEngine.getCopyOfAuthorityWithSyncStatus( 1730 new SyncStorageEngine.EndPoint( 1731 account.account, 1732 syncAdapterType.type.authority, 1733 account.userId)); 1734 SyncStorageEngine.AuthorityInfo settings = syncAuthoritySyncStatus.first; 1735 SyncStatusInfo status = syncAuthoritySyncStatus.second; 1736 String authority = settings.target.provider; 1737 if (authority.length() > 50) { 1738 authority = authority.substring(authority.length() - 50); 1739 } 1740 table.set(row, 0, authority, settings.syncable, settings.enabled); 1741 table.set(row, 4, 1742 status.numSourceLocal, 1743 status.numSourcePoll, 1744 status.numSourcePeriodic, 1745 status.numSourceServer, 1746 status.numSourceUser, 1747 status.numSyncs, 1748 DateUtils.formatElapsedTime(status.totalElapsedTime / 1000)); 1749 1750 int row1 = row; 1751 if (settings.delayUntil > now) { 1752 table.set(row1++, 12, "D: " + (settings.delayUntil - now) / 1000); 1753 if (settings.backoffTime > now) { 1754 table.set(row1++, 12, "B: " + (settings.backoffTime - now) / 1000); 1755 table.set(row1++, 12, settings.backoffDelay / 1000); 1756 } 1757 } 1758 1759 if (status.lastSuccessTime != 0) { 1760 table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastSuccessSource] 1761 + " " + "SUCCESS"); 1762 table.set(row1++, 11, formatTime(status.lastSuccessTime)); 1763 } 1764 if (status.lastFailureTime != 0) { 1765 table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastFailureSource] 1766 + " " + "FAILURE"); 1767 table.set(row1++, 11, formatTime(status.lastFailureTime)); 1768 //noinspection UnusedAssignment 1769 table.set(row1++, 11, status.lastFailureMesg); 1770 } 1771 } 1772 table.writeTo(pw); 1773 } 1774 } 1775 dumpTimeSec(PrintWriter pw, long time)1776 private void dumpTimeSec(PrintWriter pw, long time) { 1777 pw.print(time/1000); pw.print('.'); pw.print((time/100)%10); 1778 pw.print('s'); 1779 } 1780 dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds)1781 private void dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds) { 1782 pw.print("Success ("); pw.print(ds.successCount); 1783 if (ds.successCount > 0) { 1784 pw.print(" for "); dumpTimeSec(pw, ds.successTime); 1785 pw.print(" avg="); dumpTimeSec(pw, ds.successTime/ds.successCount); 1786 } 1787 pw.print(") Failure ("); pw.print(ds.failureCount); 1788 if (ds.failureCount > 0) { 1789 pw.print(" for "); dumpTimeSec(pw, ds.failureTime); 1790 pw.print(" avg="); dumpTimeSec(pw, ds.failureTime/ds.failureCount); 1791 } 1792 pw.println(")"); 1793 } 1794 dumpSyncHistory(PrintWriter pw)1795 protected void dumpSyncHistory(PrintWriter pw) { 1796 dumpRecentHistory(pw); 1797 dumpDayStatistics(pw); 1798 } 1799 dumpRecentHistory(PrintWriter pw)1800 private void dumpRecentHistory(PrintWriter pw) { 1801 final ArrayList<SyncStorageEngine.SyncHistoryItem> items 1802 = mSyncStorageEngine.getSyncHistory(); 1803 if (items != null && items.size() > 0) { 1804 final Map<String, AuthoritySyncStats> authorityMap = Maps.newHashMap(); 1805 long totalElapsedTime = 0; 1806 long totalTimes = 0; 1807 final int N = items.size(); 1808 1809 int maxAuthority = 0; 1810 int maxAccount = 0; 1811 for (SyncStorageEngine.SyncHistoryItem item : items) { 1812 SyncStorageEngine.AuthorityInfo authorityInfo 1813 = mSyncStorageEngine.getAuthority(item.authorityId); 1814 final String authorityName; 1815 final String accountKey; 1816 if (authorityInfo != null) { 1817 authorityName = authorityInfo.target.provider; 1818 accountKey = authorityInfo.target.account.name + "/" 1819 + authorityInfo.target.account.type 1820 + " u" + authorityInfo.target.userId; 1821 } else { 1822 authorityName = "Unknown"; 1823 accountKey = "Unknown"; 1824 } 1825 1826 int length = authorityName.length(); 1827 if (length > maxAuthority) { 1828 maxAuthority = length; 1829 } 1830 length = accountKey.length(); 1831 if (length > maxAccount) { 1832 maxAccount = length; 1833 } 1834 1835 final long elapsedTime = item.elapsedTime; 1836 totalElapsedTime += elapsedTime; 1837 totalTimes++; 1838 AuthoritySyncStats authoritySyncStats = authorityMap.get(authorityName); 1839 if (authoritySyncStats == null) { 1840 authoritySyncStats = new AuthoritySyncStats(authorityName); 1841 authorityMap.put(authorityName, authoritySyncStats); 1842 } 1843 authoritySyncStats.elapsedTime += elapsedTime; 1844 authoritySyncStats.times++; 1845 final Map<String, AccountSyncStats> accountMap = authoritySyncStats.accountMap; 1846 AccountSyncStats accountSyncStats = accountMap.get(accountKey); 1847 if (accountSyncStats == null) { 1848 accountSyncStats = new AccountSyncStats(accountKey); 1849 accountMap.put(accountKey, accountSyncStats); 1850 } 1851 accountSyncStats.elapsedTime += elapsedTime; 1852 accountSyncStats.times++; 1853 1854 } 1855 1856 if (totalElapsedTime > 0) { 1857 pw.println(); 1858 pw.printf("Detailed Statistics (Recent history): " 1859 + "%d (# of times) %ds (sync time)\n", 1860 totalTimes, totalElapsedTime / 1000); 1861 1862 final List<AuthoritySyncStats> sortedAuthorities = 1863 new ArrayList<AuthoritySyncStats>(authorityMap.values()); 1864 Collections.sort(sortedAuthorities, new Comparator<AuthoritySyncStats>() { 1865 @Override 1866 public int compare(AuthoritySyncStats lhs, AuthoritySyncStats rhs) { 1867 // reverse order 1868 int compare = Integer.compare(rhs.times, lhs.times); 1869 if (compare == 0) { 1870 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime); 1871 } 1872 return compare; 1873 } 1874 }); 1875 1876 final int maxLength = Math.max(maxAuthority, maxAccount + 3); 1877 final int padLength = 2 + 2 + maxLength + 2 + 10 + 11; 1878 final char chars[] = new char[padLength]; 1879 Arrays.fill(chars, '-'); 1880 final String separator = new String(chars); 1881 1882 final String authorityFormat = 1883 String.format(" %%-%ds: %%-9s %%-11s\n", maxLength + 2); 1884 final String accountFormat = 1885 String.format(" %%-%ds: %%-9s %%-11s\n", maxLength); 1886 1887 pw.println(separator); 1888 for (AuthoritySyncStats authoritySyncStats : sortedAuthorities) { 1889 String name = authoritySyncStats.name; 1890 long elapsedTime; 1891 int times; 1892 String timeStr; 1893 String timesStr; 1894 1895 elapsedTime = authoritySyncStats.elapsedTime; 1896 times = authoritySyncStats.times; 1897 timeStr = String.format("%ds/%d%%", 1898 elapsedTime / 1000, 1899 elapsedTime * 100 / totalElapsedTime); 1900 timesStr = String.format("%d/%d%%", 1901 times, 1902 times * 100 / totalTimes); 1903 pw.printf(authorityFormat, name, timesStr, timeStr); 1904 1905 final List<AccountSyncStats> sortedAccounts = 1906 new ArrayList<AccountSyncStats>( 1907 authoritySyncStats.accountMap.values()); 1908 Collections.sort(sortedAccounts, new Comparator<AccountSyncStats>() { 1909 @Override 1910 public int compare(AccountSyncStats lhs, AccountSyncStats rhs) { 1911 // reverse order 1912 int compare = Integer.compare(rhs.times, lhs.times); 1913 if (compare == 0) { 1914 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime); 1915 } 1916 return compare; 1917 } 1918 }); 1919 for (AccountSyncStats stats: sortedAccounts) { 1920 elapsedTime = stats.elapsedTime; 1921 times = stats.times; 1922 timeStr = String.format("%ds/%d%%", 1923 elapsedTime / 1000, 1924 elapsedTime * 100 / totalElapsedTime); 1925 timesStr = String.format("%d/%d%%", 1926 times, 1927 times * 100 / totalTimes); 1928 pw.printf(accountFormat, stats.name, timesStr, timeStr); 1929 } 1930 pw.println(separator); 1931 } 1932 } 1933 1934 pw.println(); 1935 pw.println("Recent Sync History"); 1936 final String format = " %-" + maxAccount + "s %-" + maxAuthority + "s %s\n"; 1937 final Map<String, Long> lastTimeMap = Maps.newHashMap(); 1938 final PackageManager pm = mContext.getPackageManager(); 1939 for (int i = 0; i < N; i++) { 1940 SyncStorageEngine.SyncHistoryItem item = items.get(i); 1941 SyncStorageEngine.AuthorityInfo authorityInfo 1942 = mSyncStorageEngine.getAuthority(item.authorityId); 1943 final String authorityName; 1944 final String accountKey; 1945 if (authorityInfo != null) { 1946 authorityName = authorityInfo.target.provider; 1947 accountKey = authorityInfo.target.account.name + "/" 1948 + authorityInfo.target.account.type 1949 + " u" + authorityInfo.target.userId; 1950 } else { 1951 authorityName = "Unknown"; 1952 accountKey = "Unknown"; 1953 } 1954 final long elapsedTime = item.elapsedTime; 1955 final Time time = new Time(); 1956 final long eventTime = item.eventTime; 1957 time.set(eventTime); 1958 1959 final String key = authorityName + "/" + accountKey; 1960 final Long lastEventTime = lastTimeMap.get(key); 1961 final String diffString; 1962 if (lastEventTime == null) { 1963 diffString = ""; 1964 } else { 1965 final long diff = (lastEventTime - eventTime) / 1000; 1966 if (diff < 60) { 1967 diffString = String.valueOf(diff); 1968 } else if (diff < 3600) { 1969 diffString = String.format("%02d:%02d", diff / 60, diff % 60); 1970 } else { 1971 final long sec = diff % 3600; 1972 diffString = String.format("%02d:%02d:%02d", 1973 diff / 3600, sec / 60, sec % 60); 1974 } 1975 } 1976 lastTimeMap.put(key, eventTime); 1977 1978 pw.printf(" #%-3d: %s %8s %5.1fs %8s", 1979 i + 1, 1980 formatTime(eventTime), 1981 SyncStorageEngine.SOURCES[item.source], 1982 ((float) elapsedTime) / 1000, 1983 diffString); 1984 pw.printf(format, accountKey, authorityName, 1985 SyncOperation.reasonToString(pm, item.reason)); 1986 1987 if (item.event != SyncStorageEngine.EVENT_STOP 1988 || item.upstreamActivity != 0 1989 || item.downstreamActivity != 0) { 1990 pw.printf(" event=%d upstreamActivity=%d downstreamActivity=%d\n", 1991 item.event, 1992 item.upstreamActivity, 1993 item.downstreamActivity); 1994 } 1995 if (item.mesg != null 1996 && !SyncStorageEngine.MESG_SUCCESS.equals(item.mesg)) { 1997 pw.printf(" mesg=%s\n", item.mesg); 1998 } 1999 } 2000 pw.println(); 2001 pw.println("Recent Sync History Extras"); 2002 for (int i = 0; i < N; i++) { 2003 final SyncStorageEngine.SyncHistoryItem item = items.get(i); 2004 final Bundle extras = item.extras; 2005 if (extras == null || extras.size() == 0) { 2006 continue; 2007 } 2008 final SyncStorageEngine.AuthorityInfo authorityInfo 2009 = mSyncStorageEngine.getAuthority(item.authorityId); 2010 final String authorityName; 2011 final String accountKey; 2012 if (authorityInfo != null) { 2013 authorityName = authorityInfo.target.provider; 2014 accountKey = authorityInfo.target.account.name + "/" 2015 + authorityInfo.target.account.type 2016 + " u" + authorityInfo.target.userId; 2017 } else { 2018 authorityName = "Unknown"; 2019 accountKey = "Unknown"; 2020 } 2021 final Time time = new Time(); 2022 final long eventTime = item.eventTime; 2023 time.set(eventTime); 2024 2025 pw.printf(" #%-3d: %s %8s ", 2026 i + 1, 2027 formatTime(eventTime), 2028 SyncStorageEngine.SOURCES[item.source]); 2029 2030 pw.printf(format, accountKey, authorityName, extras); 2031 } 2032 } 2033 } 2034 dumpDayStatistics(PrintWriter pw)2035 private void dumpDayStatistics(PrintWriter pw) { 2036 SyncStorageEngine.DayStats dses[] = mSyncStorageEngine.getDayStatistics(); 2037 if (dses != null && dses[0] != null) { 2038 pw.println(); 2039 pw.println("Sync Statistics"); 2040 pw.print(" Today: "); dumpDayStatistic(pw, dses[0]); 2041 int today = dses[0].day; 2042 int i; 2043 SyncStorageEngine.DayStats ds; 2044 2045 // Print each day in the current week. 2046 for (i=1; i<=6 && i < dses.length; i++) { 2047 ds = dses[i]; 2048 if (ds == null) break; 2049 int delta = today-ds.day; 2050 if (delta > 6) break; 2051 2052 pw.print(" Day-"); pw.print(delta); pw.print(": "); 2053 dumpDayStatistic(pw, ds); 2054 } 2055 2056 // Aggregate all following days into weeks and print totals. 2057 int weekDay = today; 2058 while (i < dses.length) { 2059 SyncStorageEngine.DayStats aggr = null; 2060 weekDay -= 7; 2061 while (i < dses.length) { 2062 ds = dses[i]; 2063 if (ds == null) { 2064 i = dses.length; 2065 break; 2066 } 2067 int delta = weekDay-ds.day; 2068 if (delta > 6) break; 2069 i++; 2070 2071 if (aggr == null) { 2072 aggr = new SyncStorageEngine.DayStats(weekDay); 2073 } 2074 aggr.successCount += ds.successCount; 2075 aggr.successTime += ds.successTime; 2076 aggr.failureCount += ds.failureCount; 2077 aggr.failureTime += ds.failureTime; 2078 } 2079 if (aggr != null) { 2080 pw.print(" Week-"); pw.print((today-weekDay)/7); pw.print(": "); 2081 dumpDayStatistic(pw, aggr); 2082 } 2083 } 2084 } 2085 } 2086 dumpSyncAdapters(IndentingPrintWriter pw)2087 private void dumpSyncAdapters(IndentingPrintWriter pw) { 2088 pw.println(); 2089 final List<UserInfo> users = getAllUsers(); 2090 if (users != null) { 2091 for (UserInfo user : users) { 2092 pw.println("Sync adapters for " + user + ":"); 2093 pw.increaseIndent(); 2094 for (RegisteredServicesCache.ServiceInfo<?> info : 2095 mSyncAdapters.getAllServices(user.id)) { 2096 pw.println(info); 2097 } 2098 pw.decreaseIndent(); 2099 pw.println(); 2100 } 2101 } 2102 } 2103 2104 private static class AuthoritySyncStats { 2105 String name; 2106 long elapsedTime; 2107 int times; 2108 Map<String, AccountSyncStats> accountMap = Maps.newHashMap(); 2109 AuthoritySyncStats(String name)2110 private AuthoritySyncStats(String name) { 2111 this.name = name; 2112 } 2113 } 2114 2115 private static class AccountSyncStats { 2116 String name; 2117 long elapsedTime; 2118 int times; 2119 AccountSyncStats(String name)2120 private AccountSyncStats(String name) { 2121 this.name = name; 2122 } 2123 } 2124 2125 /** 2126 * A helper object to keep track of the time we have spent syncing since the last boot 2127 */ 2128 private class SyncTimeTracker { 2129 /** True if a sync was in progress on the most recent call to update() */ 2130 boolean mLastWasSyncing = false; 2131 /** Used to track when lastWasSyncing was last set */ 2132 long mWhenSyncStarted = 0; 2133 /** The cumulative time we have spent syncing */ 2134 private long mTimeSpentSyncing; 2135 2136 /** Call to let the tracker know that the sync state may have changed */ update()2137 public synchronized void update() { 2138 final boolean isSyncInProgress = !mActiveSyncContexts.isEmpty(); 2139 if (isSyncInProgress == mLastWasSyncing) return; 2140 final long now = SystemClock.elapsedRealtime(); 2141 if (isSyncInProgress) { 2142 mWhenSyncStarted = now; 2143 } else { 2144 mTimeSpentSyncing += now - mWhenSyncStarted; 2145 } 2146 mLastWasSyncing = isSyncInProgress; 2147 } 2148 2149 /** Get how long we have been syncing, in ms */ timeSpentSyncing()2150 public synchronized long timeSpentSyncing() { 2151 if (!mLastWasSyncing) return mTimeSpentSyncing; 2152 2153 final long now = SystemClock.elapsedRealtime(); 2154 return mTimeSpentSyncing + (now - mWhenSyncStarted); 2155 } 2156 } 2157 2158 class ServiceConnectionData { 2159 public final ActiveSyncContext activeSyncContext; 2160 public final IBinder adapter; 2161 ServiceConnectionData(ActiveSyncContext activeSyncContext, IBinder adapter)2162 ServiceConnectionData(ActiveSyncContext activeSyncContext, IBinder adapter) { 2163 this.activeSyncContext = activeSyncContext; 2164 this.adapter = adapter; 2165 } 2166 } 2167 2168 /** 2169 * Handles SyncOperation Messages that are posted to the associated 2170 * HandlerThread. 2171 */ 2172 class SyncHandler extends Handler { 2173 // Messages that can be sent on mHandler. 2174 private static final int MESSAGE_SYNC_FINISHED = 1; 2175 private static final int MESSAGE_RELEASE_MESSAGES_FROM_QUEUE = 2; 2176 private static final int MESSAGE_SERVICE_CONNECTED = 4; 2177 private static final int MESSAGE_SERVICE_DISCONNECTED = 5; 2178 private static final int MESSAGE_CANCEL = 6; 2179 static final int MESSAGE_JOBSERVICE_OBJECT = 7; 2180 static final int MESSAGE_START_SYNC = 10; 2181 static final int MESSAGE_STOP_SYNC = 11; 2182 static final int MESSAGE_SCHEDULE_SYNC = 12; 2183 static final int MESSAGE_UPDATE_PERIODIC_SYNC = 13; 2184 static final int MESSAGE_REMOVE_PERIODIC_SYNC = 14; 2185 2186 /** 2187 * Posted periodically to monitor network process for long-running syncs. 2188 * obj: {@link com.android.server.content.SyncManager.ActiveSyncContext} 2189 */ 2190 private static final int MESSAGE_MONITOR_SYNC = 8; 2191 private static final int MESSAGE_ACCOUNTS_UPDATED = 9; 2192 2193 public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker(); 2194 private final HashMap<String, PowerManager.WakeLock> mWakeLocks = Maps.newHashMap(); 2195 2196 private List<Message> mUnreadyQueue = new ArrayList<Message>(); 2197 onBootCompleted()2198 void onBootCompleted() { 2199 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2200 Slog.v(TAG, "Boot completed."); 2201 } 2202 checkIfDeviceReady(); 2203 } 2204 onDeviceProvisioned()2205 void onDeviceProvisioned() { 2206 if (Log.isLoggable(TAG, Log.DEBUG)) { 2207 Log.d(TAG, "mProvisioned=" + mProvisioned); 2208 } 2209 checkIfDeviceReady(); 2210 } 2211 checkIfDeviceReady()2212 void checkIfDeviceReady() { 2213 if (mProvisioned && mBootCompleted && mJobServiceReady) { 2214 synchronized(this) { 2215 mSyncStorageEngine.restoreAllPeriodicSyncs(); 2216 // Dispatch any stashed messages. 2217 obtainMessage(MESSAGE_RELEASE_MESSAGES_FROM_QUEUE).sendToTarget(); 2218 } 2219 } 2220 } 2221 2222 /** 2223 * Stash any messages that come to the handler before boot is complete or before the device 2224 * is properly provisioned (i.e. out of set-up wizard). 2225 * {@link #onBootCompleted()} and {@link SyncHandler#onDeviceProvisioned} both 2226 * need to come in before we start syncing. 2227 * @param msg Message to dispatch at a later point. 2228 * @return true if a message was enqueued, false otherwise. This is to avoid losing the 2229 * message if we manage to acquire the lock but by the time we do boot has completed. 2230 */ tryEnqueueMessageUntilReadyToRun(Message msg)2231 private boolean tryEnqueueMessageUntilReadyToRun(Message msg) { 2232 synchronized (this) { 2233 if (!mBootCompleted || !mProvisioned || !mJobServiceReady) { 2234 // Need to copy the message bc looper will recycle it. 2235 Message m = Message.obtain(msg); 2236 mUnreadyQueue.add(m); 2237 return true; 2238 } else { 2239 return false; 2240 } 2241 } 2242 } 2243 SyncHandler(Looper looper)2244 public SyncHandler(Looper looper) { 2245 super(looper); 2246 } 2247 handleMessage(Message msg)2248 public void handleMessage(Message msg) { 2249 try { 2250 mSyncManagerWakeLock.acquire(); 2251 // We only want to enqueue sync related messages until device is ready. 2252 // Other messages are handled without enqueuing. 2253 if (msg.what == MESSAGE_JOBSERVICE_OBJECT) { 2254 Slog.i(TAG, "Got SyncJobService instance."); 2255 mSyncJobService = (SyncJobService) msg.obj; 2256 mJobServiceReady = true; 2257 checkIfDeviceReady(); 2258 } else if (msg.what == SyncHandler.MESSAGE_ACCOUNTS_UPDATED) { 2259 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2260 Slog.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED"); 2261 } 2262 EndPoint targets = (EndPoint) msg.obj; 2263 updateRunningAccountsH(targets); 2264 } else if (msg.what == MESSAGE_RELEASE_MESSAGES_FROM_QUEUE) { 2265 if (mUnreadyQueue != null) { 2266 for (Message m : mUnreadyQueue) { 2267 handleSyncMessage(m); 2268 } 2269 mUnreadyQueue = null; 2270 } 2271 } else if (tryEnqueueMessageUntilReadyToRun(msg)) { 2272 // No work to be done. 2273 } else { 2274 handleSyncMessage(msg); 2275 } 2276 } finally { 2277 mSyncManagerWakeLock.release(); 2278 } 2279 } 2280 handleSyncMessage(Message msg)2281 private void handleSyncMessage(Message msg) { 2282 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2283 2284 try { 2285 mDataConnectionIsConnected = readDataConnectionState(); 2286 switch (msg.what) { 2287 case MESSAGE_SCHEDULE_SYNC: 2288 SyncOperation op = (SyncOperation) msg.obj; 2289 scheduleSyncOperationH(op); 2290 break; 2291 2292 case MESSAGE_START_SYNC: 2293 op = (SyncOperation) msg.obj; 2294 startSyncH(op); 2295 break; 2296 2297 case MESSAGE_STOP_SYNC: 2298 op = (SyncOperation) msg.obj; 2299 if (isLoggable) { 2300 Slog.v(TAG, "Stop sync received."); 2301 } 2302 ActiveSyncContext asc = findActiveSyncContextH(op.jobId); 2303 if (asc != null) { 2304 runSyncFinishedOrCanceledH(null /* no result */, asc); 2305 boolean reschedule = msg.arg1 != 0; 2306 boolean applyBackoff = msg.arg2 != 0; 2307 if (isLoggable) { 2308 Slog.v(TAG, "Stopping sync. Reschedule: " + reschedule 2309 + "Backoff: " + applyBackoff); 2310 } 2311 if (applyBackoff) { 2312 increaseBackoffSetting(op.target); 2313 } 2314 if (reschedule) { 2315 deferStoppedSyncH(op, 0); 2316 } 2317 } 2318 break; 2319 2320 case MESSAGE_UPDATE_PERIODIC_SYNC: 2321 UpdatePeriodicSyncMessagePayload data = 2322 (UpdatePeriodicSyncMessagePayload) msg.obj; 2323 updateOrAddPeriodicSyncH(data.target, data.pollFrequency, 2324 data.flex, data.extras); 2325 break; 2326 case MESSAGE_REMOVE_PERIODIC_SYNC: 2327 removePeriodicSyncH((EndPoint)msg.obj, msg.getData()); 2328 break; 2329 2330 case SyncHandler.MESSAGE_CANCEL: 2331 SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj; 2332 Bundle extras = msg.peekData(); 2333 if (Log.isLoggable(TAG, Log.DEBUG)) { 2334 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_CANCEL: " 2335 + endpoint + " bundle: " + extras); 2336 } 2337 cancelActiveSyncH(endpoint, extras); 2338 break; 2339 2340 case SyncHandler.MESSAGE_SYNC_FINISHED: 2341 SyncFinishedOrCancelledMessagePayload payload = 2342 (SyncFinishedOrCancelledMessagePayload) msg.obj; 2343 if (!isSyncStillActiveH(payload.activeSyncContext)) { 2344 Log.d(TAG, "handleSyncHandlerMessage: dropping since the " 2345 + "sync is no longer active: " 2346 + payload.activeSyncContext); 2347 break; 2348 } 2349 if (isLoggable) { 2350 Slog.v(TAG, "syncFinished" + payload.activeSyncContext.mSyncOperation); 2351 } 2352 mSyncJobService.callJobFinished( 2353 payload.activeSyncContext.mSyncOperation.jobId, false); 2354 runSyncFinishedOrCanceledH(payload.syncResult, 2355 payload.activeSyncContext); 2356 break; 2357 2358 case SyncHandler.MESSAGE_SERVICE_CONNECTED: { 2359 ServiceConnectionData msgData = (ServiceConnectionData) msg.obj; 2360 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2361 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: " 2362 + msgData.activeSyncContext); 2363 } 2364 // Check that this isn't an old message. 2365 if (isSyncStillActiveH(msgData.activeSyncContext)) { 2366 runBoundToAdapterH( 2367 msgData.activeSyncContext, 2368 msgData.adapter); 2369 } 2370 break; 2371 } 2372 2373 case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: { 2374 final ActiveSyncContext currentSyncContext = 2375 ((ServiceConnectionData) msg.obj).activeSyncContext; 2376 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2377 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: " 2378 + currentSyncContext); 2379 } 2380 // Check that this isn't an old message. 2381 if (isSyncStillActiveH(currentSyncContext)) { 2382 // cancel the sync if we have a syncadapter, which means one is 2383 // outstanding 2384 try { 2385 if (currentSyncContext.mSyncAdapter != null) { 2386 currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext); 2387 } 2388 } catch (RemoteException e) { 2389 // We don't need to retry this in this case. 2390 } 2391 2392 // Pretend that the sync failed with an IOException, 2393 // which is a soft error. 2394 SyncResult syncResult = new SyncResult(); 2395 syncResult.stats.numIoExceptions++; 2396 mSyncJobService.callJobFinished( 2397 currentSyncContext.mSyncOperation.jobId, false); 2398 runSyncFinishedOrCanceledH(syncResult, currentSyncContext); 2399 } 2400 break; 2401 } 2402 2403 case SyncHandler.MESSAGE_MONITOR_SYNC: 2404 ActiveSyncContext monitoredSyncContext = (ActiveSyncContext) msg.obj; 2405 if (Log.isLoggable(TAG, Log.DEBUG)) { 2406 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_MONITOR_SYNC: " + 2407 monitoredSyncContext.mSyncOperation.target); 2408 } 2409 2410 if (isSyncNotUsingNetworkH(monitoredSyncContext)) { 2411 Log.w(TAG, String.format( 2412 "Detected sync making no progress for %s. cancelling.", 2413 monitoredSyncContext)); 2414 mSyncJobService.callJobFinished( 2415 monitoredSyncContext.mSyncOperation.jobId, false); 2416 runSyncFinishedOrCanceledH( 2417 null /* cancel => no result */, monitoredSyncContext); 2418 } else { 2419 // Repost message to check again. 2420 postMonitorSyncProgressMessage(monitoredSyncContext); 2421 } 2422 break; 2423 2424 } 2425 } finally { 2426 mSyncTimeTracker.update(); 2427 } 2428 } 2429 getSyncWakeLock(SyncOperation operation)2430 private PowerManager.WakeLock getSyncWakeLock(SyncOperation operation) { 2431 final String wakeLockKey = operation.wakeLockName(); 2432 PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey); 2433 if (wakeLock == null) { 2434 final String name = SYNC_WAKE_LOCK_PREFIX + wakeLockKey; 2435 wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name); 2436 wakeLock.setReferenceCounted(false); 2437 mWakeLocks.put(wakeLockKey, wakeLock); 2438 } 2439 return wakeLock; 2440 } 2441 2442 /** 2443 * Defer the specified SyncOperation by rescheduling it on the JobScheduler with some 2444 * delay. This is equivalent to a failure. If this is a periodic sync, a delayed one-off 2445 * sync will be scheduled. 2446 */ deferSyncH(SyncOperation op, long delay)2447 private void deferSyncH(SyncOperation op, long delay) { 2448 mSyncJobService.callJobFinished(op.jobId, false); 2449 if (op.isPeriodic) { 2450 scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay); 2451 } else { 2452 // mSyncJobService.callJobFinished is async, so cancel the job to ensure we don't 2453 // find the this job in the pending jobs list while looking for duplicates 2454 // before scheduling it at a later time. 2455 getJobScheduler().cancel(op.jobId); 2456 scheduleSyncOperationH(op, delay); 2457 } 2458 } 2459 2460 /* Same as deferSyncH, but assumes that job is no longer running on JobScheduler. */ deferStoppedSyncH(SyncOperation op, long delay)2461 private void deferStoppedSyncH(SyncOperation op, long delay) { 2462 if (op.isPeriodic) { 2463 scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay); 2464 } else { 2465 scheduleSyncOperationH(op, delay); 2466 } 2467 } 2468 2469 /** 2470 * Cancel an active sync and reschedule it on the JobScheduler with some delay. 2471 */ deferActiveSyncH(ActiveSyncContext asc)2472 private void deferActiveSyncH(ActiveSyncContext asc) { 2473 SyncOperation op = asc.mSyncOperation; 2474 runSyncFinishedOrCanceledH(null, asc); 2475 deferSyncH(op, SYNC_DELAY_ON_CONFLICT); 2476 } 2477 startSyncH(SyncOperation op)2478 private void startSyncH(SyncOperation op) { 2479 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2480 if (isLoggable) Slog.v(TAG, op.toString()); 2481 2482 if (mStorageIsLow) { 2483 deferSyncH(op, SYNC_DELAY_ON_LOW_STORAGE); 2484 return; 2485 } 2486 2487 if (op.isPeriodic) { 2488 // Don't allow this periodic to run if a previous instance failed and is currently 2489 // scheduled according to some backoff criteria. 2490 List<SyncOperation> ops = getAllPendingSyncs(); 2491 for (SyncOperation syncOperation: ops) { 2492 if (syncOperation.sourcePeriodicId == op.jobId) { 2493 mSyncJobService.callJobFinished(op.jobId, false); 2494 return; 2495 } 2496 } 2497 // Don't allow this periodic to run if a previous instance failed and is currently 2498 // executing according to some backoff criteria. 2499 for (ActiveSyncContext asc: mActiveSyncContexts) { 2500 if (asc.mSyncOperation.sourcePeriodicId == op.jobId) { 2501 mSyncJobService.callJobFinished(op.jobId, false); 2502 return; 2503 } 2504 } 2505 // Check for adapter delays. 2506 if (isAdapterDelayed(op.target)) { 2507 deferSyncH(op, 0 /* No minimum delay */); 2508 return; 2509 } 2510 } 2511 2512 // Check for conflicting syncs. 2513 for (ActiveSyncContext asc: mActiveSyncContexts) { 2514 if (asc.mSyncOperation.isConflict(op)) { 2515 // If the provided SyncOperation conflicts with a running one, the lower 2516 // priority sync is pre-empted. 2517 if (asc.mSyncOperation.findPriority() >= op.findPriority()) { 2518 if (isLoggable) { 2519 Slog.v(TAG, "Rescheduling sync due to conflict " + op.toString()); 2520 } 2521 deferSyncH(op, SYNC_DELAY_ON_CONFLICT); 2522 return; 2523 } else { 2524 if (isLoggable) { 2525 Slog.v(TAG, "Pushing back running sync due to a higher priority sync"); 2526 } 2527 deferActiveSyncH(asc); 2528 break; 2529 } 2530 } 2531 } 2532 2533 if (isOperationValid(op)) { 2534 if (!dispatchSyncOperation(op)) { 2535 mSyncJobService.callJobFinished(op.jobId, false); 2536 } 2537 } else { 2538 mSyncJobService.callJobFinished(op.jobId, false); 2539 } 2540 setAuthorityPendingState(op.target); 2541 } 2542 findActiveSyncContextH(int jobId)2543 private ActiveSyncContext findActiveSyncContextH(int jobId) { 2544 for (ActiveSyncContext asc: mActiveSyncContexts) { 2545 SyncOperation op = asc.mSyncOperation; 2546 if (op != null && op.jobId == jobId) { 2547 return asc; 2548 } 2549 } 2550 return null; 2551 } 2552 updateRunningAccountsH(EndPoint syncTargets)2553 private void updateRunningAccountsH(EndPoint syncTargets) { 2554 AccountAndUser[] oldAccounts = mRunningAccounts; 2555 mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts(); 2556 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2557 Slog.v(TAG, "Accounts list: "); 2558 for (AccountAndUser acc : mRunningAccounts) { 2559 Slog.v(TAG, acc.toString()); 2560 } 2561 } 2562 if (mBootCompleted) { 2563 doDatabaseCleanup(); 2564 } 2565 2566 AccountAndUser[] accounts = mRunningAccounts; 2567 for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) { 2568 if (!containsAccountAndUser(accounts, 2569 currentSyncContext.mSyncOperation.target.account, 2570 currentSyncContext.mSyncOperation.target.userId)) { 2571 Log.d(TAG, "canceling sync since the account is no longer running"); 2572 sendSyncFinishedOrCanceledMessage(currentSyncContext, 2573 null /* no result since this is a cancel */); 2574 } 2575 } 2576 2577 // On account add, check if there are any settings to be restored. 2578 for (AccountAndUser aau : mRunningAccounts) { 2579 if (!containsAccountAndUser(oldAccounts, aau.account, aau.userId)) { 2580 if (Log.isLoggable(TAG, Log.DEBUG)) { 2581 Log.d(TAG, "Account " + aau.account + " added, checking sync restore data"); 2582 } 2583 AccountSyncSettingsBackupHelper.accountAdded(mContext); 2584 break; 2585 } 2586 } 2587 2588 // Cancel all jobs from non-existent accounts. 2589 AccountAndUser[] allAccounts = AccountManagerService.getSingleton().getAllAccounts(); 2590 List<SyncOperation> ops = getAllPendingSyncs(); 2591 for (SyncOperation op: ops) { 2592 if (!containsAccountAndUser(allAccounts, op.target.account, op.target.userId)) { 2593 getJobScheduler().cancel(op.jobId); 2594 } 2595 } 2596 2597 if (syncTargets != null) { 2598 scheduleSync(syncTargets.account, syncTargets.userId, 2599 SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider, null, 0, 0, 2600 true); 2601 } 2602 } 2603 2604 /** 2605 * The given SyncOperation will be removed and a new one scheduled in its place if 2606 * an updated period or flex is specified. 2607 * @param syncOperation SyncOperation whose period and flex is to be updated. 2608 * @param pollFrequencyMillis new period in milliseconds. 2609 * @param flexMillis new flex time in milliseconds. 2610 */ maybeUpdateSyncPeriodH(SyncOperation syncOperation, long pollFrequencyMillis, long flexMillis)2611 private void maybeUpdateSyncPeriodH(SyncOperation syncOperation, long pollFrequencyMillis, 2612 long flexMillis) { 2613 if (!(pollFrequencyMillis == syncOperation.periodMillis 2614 && flexMillis == syncOperation.flexMillis)) { 2615 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2616 Slog.v(TAG, "updating period " + syncOperation + " to " + pollFrequencyMillis 2617 + " and flex to " + flexMillis); 2618 } 2619 SyncOperation newOp = new SyncOperation(syncOperation, pollFrequencyMillis, 2620 flexMillis); 2621 newOp.jobId = syncOperation.jobId; 2622 scheduleSyncOperationH(newOp); 2623 } 2624 } 2625 updateOrAddPeriodicSyncH(EndPoint target, long pollFrequency, long flex, Bundle extras)2626 private void updateOrAddPeriodicSyncH(EndPoint target, long pollFrequency, long flex, 2627 Bundle extras) { 2628 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2629 verifyJobScheduler(); // Will fill in mScheduledSyncs cache if it is not already filled. 2630 final long pollFrequencyMillis = pollFrequency * 1000L; 2631 final long flexMillis = flex * 1000L; 2632 if (isLoggable) { 2633 Slog.v(TAG, "Addition to periodic syncs requested: " + target 2634 + " period: " + pollFrequency 2635 + " flexMillis: " + flex 2636 + " extras: " + extras.toString()); 2637 } 2638 List<SyncOperation> ops = getAllPendingSyncs(); 2639 for (SyncOperation op: ops) { 2640 if (op.isPeriodic && op.target.matchesSpec(target) 2641 && syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) { 2642 maybeUpdateSyncPeriodH(op, pollFrequencyMillis, flexMillis); 2643 return; 2644 } 2645 } 2646 2647 if (isLoggable) { 2648 Slog.v(TAG, "Adding new periodic sync: " + target 2649 + " period: " + pollFrequency 2650 + " flexMillis: " + flex 2651 + " extras: " + extras.toString()); 2652 } 2653 2654 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> 2655 syncAdapterInfo = mSyncAdapters.getServiceInfo( 2656 SyncAdapterType.newKey( 2657 target.provider, target.account.type), 2658 target.userId); 2659 if (syncAdapterInfo == null) { 2660 return; 2661 } 2662 2663 SyncOperation op = new SyncOperation(target, syncAdapterInfo.uid, 2664 syncAdapterInfo.componentName.getPackageName(), SyncOperation.REASON_PERIODIC, 2665 SyncStorageEngine.SOURCE_PERIODIC, extras, 2666 syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID, 2667 pollFrequencyMillis, flexMillis); 2668 scheduleSyncOperationH(op); 2669 mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); 2670 } 2671 2672 /** 2673 * Remove this periodic sync operation and all one-off operations initiated by it. 2674 */ removePeriodicSyncInternalH(SyncOperation syncOperation)2675 private void removePeriodicSyncInternalH(SyncOperation syncOperation) { 2676 // Remove this periodic sync and all one-off syncs initiated by it. 2677 List<SyncOperation> ops = getAllPendingSyncs(); 2678 for (SyncOperation op: ops) { 2679 if (op.sourcePeriodicId == syncOperation.jobId || op.jobId == syncOperation.jobId) { 2680 ActiveSyncContext asc = findActiveSyncContextH(syncOperation.jobId); 2681 if (asc != null) { 2682 mSyncJobService.callJobFinished(syncOperation.jobId, false); 2683 runSyncFinishedOrCanceledH(null, asc); 2684 } 2685 getJobScheduler().cancel(op.jobId); 2686 } 2687 } 2688 } 2689 removePeriodicSyncH(EndPoint target, Bundle extras)2690 private void removePeriodicSyncH(EndPoint target, Bundle extras) { 2691 verifyJobScheduler(); 2692 List<SyncOperation> ops = getAllPendingSyncs(); 2693 for (SyncOperation op: ops) { 2694 if (op.isPeriodic && op.target.matchesSpec(target) 2695 && syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) { 2696 removePeriodicSyncInternalH(op); 2697 } 2698 } 2699 } 2700 isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext)2701 private boolean isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext) { 2702 final long bytesTransferredCurrent = 2703 getTotalBytesTransferredByUid(activeSyncContext.mSyncAdapterUid); 2704 final long deltaBytesTransferred = 2705 bytesTransferredCurrent - activeSyncContext.mBytesTransferredAtLastPoll; 2706 2707 if (Log.isLoggable(TAG, Log.DEBUG)) { 2708 // Bytes transferred 2709 long remainder = deltaBytesTransferred; 2710 long mb = remainder / (1024 * 1024); 2711 remainder %= 1024 * 1024; 2712 long kb = remainder / 1024; 2713 remainder %= 1024; 2714 long b = remainder; 2715 Log.d(TAG, String.format( 2716 "Time since last update: %ds. Delta transferred: %dMBs,%dKBs,%dBs", 2717 (SystemClock.elapsedRealtime() 2718 - activeSyncContext.mLastPolledTimeElapsed)/1000, 2719 mb, kb, b) 2720 ); 2721 } 2722 return (deltaBytesTransferred <= SYNC_MONITOR_PROGRESS_THRESHOLD_BYTES); 2723 } 2724 2725 /** 2726 * Determine if a sync is no longer valid and should be dropped. 2727 */ isOperationValid(SyncOperation op)2728 private boolean isOperationValid(SyncOperation op) { 2729 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2730 int state; 2731 final EndPoint target = op.target; 2732 boolean syncEnabled = mSyncStorageEngine.getMasterSyncAutomatically(target.userId); 2733 // Drop the sync if the account of this operation no longer exists. 2734 AccountAndUser[] accounts = mRunningAccounts; 2735 if (!containsAccountAndUser(accounts, target.account, target.userId)) { 2736 if (isLoggable) { 2737 Slog.v(TAG, " Dropping sync operation: account doesn't exist."); 2738 } 2739 return false; 2740 } 2741 // Drop this sync request if it isn't syncable. 2742 state = getIsSyncable(target.account, target.userId, target.provider); 2743 if (state == 0) { 2744 if (isLoggable) { 2745 Slog.v(TAG, " Dropping sync operation: isSyncable == 0."); 2746 } 2747 return false; 2748 } 2749 syncEnabled = syncEnabled && mSyncStorageEngine.getSyncAutomatically( 2750 target.account, target.userId, target.provider); 2751 2752 // We ignore system settings that specify the sync is invalid if: 2753 // 1) It's manual - we try it anyway. When/if it fails it will be rescheduled. 2754 // or 2755 // 2) it's an initialisation sync - we just need to connect to it. 2756 final boolean ignoreSystemConfiguration = op.isIgnoreSettings() || (state < 0); 2757 2758 // Sync not enabled. 2759 if (!syncEnabled && !ignoreSystemConfiguration) { 2760 if (isLoggable) { 2761 Slog.v(TAG, " Dropping sync operation: disallowed by settings/network."); 2762 } 2763 return false; 2764 } 2765 return true; 2766 } 2767 dispatchSyncOperation(SyncOperation op)2768 private boolean dispatchSyncOperation(SyncOperation op) { 2769 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2770 Slog.v(TAG, "dispatchSyncOperation: we are going to sync " + op); 2771 Slog.v(TAG, "num active syncs: " + mActiveSyncContexts.size()); 2772 for (ActiveSyncContext syncContext : mActiveSyncContexts) { 2773 Slog.v(TAG, syncContext.toString()); 2774 } 2775 } 2776 // Connect to the sync adapter. 2777 int targetUid; 2778 ComponentName targetComponent; 2779 final SyncStorageEngine.EndPoint info = op.target; 2780 SyncAdapterType syncAdapterType = 2781 SyncAdapterType.newKey(info.provider, info.account.type); 2782 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; 2783 syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, info.userId); 2784 if (syncAdapterInfo == null) { 2785 Log.d(TAG, "can't find a sync adapter for " + syncAdapterType 2786 + ", removing settings for it"); 2787 mSyncStorageEngine.removeAuthority(info); 2788 return false; 2789 } 2790 targetUid = syncAdapterInfo.uid; 2791 targetComponent = syncAdapterInfo.componentName; 2792 ActiveSyncContext activeSyncContext = 2793 new ActiveSyncContext(op, insertStartSyncEvent(op), targetUid); 2794 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2795 Slog.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext); 2796 } 2797 2798 activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext); 2799 mActiveSyncContexts.add(activeSyncContext); 2800 2801 // Post message to begin monitoring this sync's progress. 2802 postMonitorSyncProgressMessage(activeSyncContext); 2803 2804 if (!activeSyncContext.bindToSyncAdapter(targetComponent, info.userId)) { 2805 Slog.e(TAG, "Bind attempt failed - target: " + targetComponent); 2806 closeActiveSyncContext(activeSyncContext); 2807 return false; 2808 } 2809 2810 return true; 2811 } 2812 runBoundToAdapterH(final ActiveSyncContext activeSyncContext, IBinder syncAdapter)2813 private void runBoundToAdapterH(final ActiveSyncContext activeSyncContext, 2814 IBinder syncAdapter) { 2815 final SyncOperation syncOperation = activeSyncContext.mSyncOperation; 2816 try { 2817 activeSyncContext.mIsLinkedToDeath = true; 2818 syncAdapter.linkToDeath(activeSyncContext, 0); 2819 2820 activeSyncContext.mSyncAdapter = ISyncAdapter.Stub.asInterface(syncAdapter); 2821 activeSyncContext.mSyncAdapter 2822 .startSync(activeSyncContext, syncOperation.target.provider, 2823 syncOperation.target.account, syncOperation.extras); 2824 } catch (RemoteException remoteExc) { 2825 Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc); 2826 closeActiveSyncContext(activeSyncContext); 2827 increaseBackoffSetting(syncOperation.target); 2828 scheduleSyncOperationH(syncOperation); 2829 } catch (RuntimeException exc) { 2830 closeActiveSyncContext(activeSyncContext); 2831 Slog.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc); 2832 } 2833 } 2834 2835 /** 2836 * Cancel the sync for the provided target that matches the given bundle. 2837 * @param info Can have null fields to indicate all the active syncs for that field. 2838 * @param extras Can be null to indicate <strong>all</strong> syncs for the given endpoint. 2839 */ cancelActiveSyncH(SyncStorageEngine.EndPoint info, Bundle extras)2840 private void cancelActiveSyncH(SyncStorageEngine.EndPoint info, Bundle extras) { 2841 ArrayList<ActiveSyncContext> activeSyncs = 2842 new ArrayList<ActiveSyncContext>(mActiveSyncContexts); 2843 for (ActiveSyncContext activeSyncContext : activeSyncs) { 2844 if (activeSyncContext != null) { 2845 final SyncStorageEngine.EndPoint opInfo = 2846 activeSyncContext.mSyncOperation.target; 2847 if (!opInfo.matchesSpec(info)) { 2848 continue; 2849 } 2850 if (extras != null && 2851 !syncExtrasEquals(activeSyncContext.mSyncOperation.extras, 2852 extras, 2853 false /* no config settings */)) { 2854 continue; 2855 } 2856 mSyncJobService.callJobFinished(activeSyncContext.mSyncOperation.jobId, false); 2857 runSyncFinishedOrCanceledH(null /* cancel => no result */, activeSyncContext); 2858 } 2859 } 2860 } 2861 2862 /** 2863 * Should be called when a one-off instance of a periodic sync completes successfully. 2864 */ reschedulePeriodicSyncH(SyncOperation syncOperation)2865 private void reschedulePeriodicSyncH(SyncOperation syncOperation) { 2866 // Ensure that the periodic sync wasn't removed. 2867 SyncOperation periodicSync = null; 2868 List<SyncOperation> ops = getAllPendingSyncs(); 2869 for (SyncOperation op: ops) { 2870 if (op.isPeriodic && syncOperation.matchesPeriodicOperation(op)) { 2871 periodicSync = op; 2872 break; 2873 } 2874 } 2875 if (periodicSync == null) { 2876 return; 2877 } 2878 scheduleSyncOperationH(periodicSync); 2879 } 2880 runSyncFinishedOrCanceledH(SyncResult syncResult, ActiveSyncContext activeSyncContext)2881 private void runSyncFinishedOrCanceledH(SyncResult syncResult, 2882 ActiveSyncContext activeSyncContext) { 2883 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2884 2885 final SyncOperation syncOperation = activeSyncContext.mSyncOperation; 2886 final SyncStorageEngine.EndPoint info = syncOperation.target; 2887 2888 if (activeSyncContext.mIsLinkedToDeath) { 2889 activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0); 2890 activeSyncContext.mIsLinkedToDeath = false; 2891 } 2892 closeActiveSyncContext(activeSyncContext); 2893 final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime; 2894 String historyMessage; 2895 int downstreamActivity; 2896 int upstreamActivity; 2897 2898 if (!syncOperation.isPeriodic) { 2899 // mSyncJobService.jobFinidhed is async, we need to ensure that this job is 2900 // removed from JobScheduler's pending jobs list before moving forward and 2901 // potentially rescheduling all pending jobs to respect new backoff values. 2902 getJobScheduler().cancel(syncOperation.jobId); 2903 } 2904 2905 if (syncResult != null) { 2906 if (isLoggable) { 2907 Slog.v(TAG, "runSyncFinishedOrCanceled [finished]: " 2908 + syncOperation + ", result " + syncResult); 2909 } 2910 2911 if (!syncResult.hasError()) { 2912 historyMessage = SyncStorageEngine.MESG_SUCCESS; 2913 // TODO: set these correctly when the SyncResult is extended to include it 2914 downstreamActivity = 0; 2915 upstreamActivity = 0; 2916 clearBackoffSetting(syncOperation.target); 2917 2918 // If the operation completes successfully and it was scheduled due to 2919 // a periodic operation failing, we reschedule the periodic operation to 2920 // start from now. 2921 if (syncOperation.isDerivedFromFailedPeriodicSync()) { 2922 reschedulePeriodicSyncH(syncOperation); 2923 } 2924 } else { 2925 Log.d(TAG, "failed sync operation " + syncOperation + ", " + syncResult); 2926 // the operation failed so increase the backoff time 2927 increaseBackoffSetting(syncOperation.target); 2928 if (!syncOperation.isPeriodic) { 2929 // reschedule the sync if so indicated by the syncResult 2930 maybeRescheduleSync(syncResult, syncOperation); 2931 } else { 2932 // create a normal sync instance that will respect adapter backoffs 2933 postScheduleSyncMessage(syncOperation.createOneTimeSyncOperation()); 2934 } 2935 historyMessage = ContentResolver.syncErrorToString( 2936 syncResultToErrorNumber(syncResult)); 2937 // TODO: set these correctly when the SyncResult is extended to include it 2938 downstreamActivity = 0; 2939 upstreamActivity = 0; 2940 } 2941 setDelayUntilTime(syncOperation.target, syncResult.delayUntil); 2942 } else { 2943 if (isLoggable) { 2944 Slog.v(TAG, "runSyncFinishedOrCanceled [canceled]: " + syncOperation); 2945 } 2946 if (activeSyncContext.mSyncAdapter != null) { 2947 try { 2948 activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext); 2949 } catch (RemoteException e) { 2950 // we don't need to retry this in this case 2951 } 2952 } 2953 historyMessage = SyncStorageEngine.MESG_CANCELED; 2954 downstreamActivity = 0; 2955 upstreamActivity = 0; 2956 } 2957 2958 stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage, 2959 upstreamActivity, downstreamActivity, elapsedTime); 2960 // Check for full-resync and schedule it after closing off the last sync. 2961 if (syncResult != null && syncResult.tooManyDeletions) { 2962 installHandleTooManyDeletesNotification(info.account, 2963 info.provider, syncResult.stats.numDeletes, 2964 info.userId); 2965 } else { 2966 mNotificationMgr.cancelAsUser(null, 2967 info.account.hashCode() ^ info.provider.hashCode(), 2968 new UserHandle(info.userId)); 2969 } 2970 if (syncResult != null && syncResult.fullSyncRequested) { 2971 scheduleSyncOperationH( 2972 new SyncOperation(info.account, info.userId, 2973 syncOperation.owningUid, syncOperation.owningPackage, 2974 syncOperation.reason, 2975 syncOperation.syncSource, info.provider, new Bundle(), 2976 syncOperation.allowParallelSyncs)); 2977 } 2978 } 2979 closeActiveSyncContext(ActiveSyncContext activeSyncContext)2980 private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) { 2981 activeSyncContext.close(); 2982 mActiveSyncContexts.remove(activeSyncContext); 2983 mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo, 2984 activeSyncContext.mSyncOperation.target.userId); 2985 2986 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2987 Slog.v(TAG, "removing all MESSAGE_MONITOR_SYNC & MESSAGE_SYNC_EXPIRED for " 2988 + activeSyncContext.toString()); 2989 } 2990 mSyncHandler.removeMessages(SyncHandler.MESSAGE_MONITOR_SYNC, activeSyncContext); 2991 } 2992 2993 /** 2994 * Convert the error-containing SyncResult into the Sync.History error number. Since 2995 * the SyncResult may indicate multiple errors at once, this method just returns the 2996 * most "serious" error. 2997 * @param syncResult the SyncResult from which to read 2998 * @return the most "serious" error set in the SyncResult 2999 * @throws IllegalStateException if the SyncResult does not indicate any errors. 3000 * If SyncResult.error() is true then it is safe to call this. 3001 */ syncResultToErrorNumber(SyncResult syncResult)3002 private int syncResultToErrorNumber(SyncResult syncResult) { 3003 if (syncResult.syncAlreadyInProgress) 3004 return ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS; 3005 if (syncResult.stats.numAuthExceptions > 0) 3006 return ContentResolver.SYNC_ERROR_AUTHENTICATION; 3007 if (syncResult.stats.numIoExceptions > 0) 3008 return ContentResolver.SYNC_ERROR_IO; 3009 if (syncResult.stats.numParseExceptions > 0) 3010 return ContentResolver.SYNC_ERROR_PARSE; 3011 if (syncResult.stats.numConflictDetectedExceptions > 0) 3012 return ContentResolver.SYNC_ERROR_CONFLICT; 3013 if (syncResult.tooManyDeletions) 3014 return ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS; 3015 if (syncResult.tooManyRetries) 3016 return ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES; 3017 if (syncResult.databaseError) 3018 return ContentResolver.SYNC_ERROR_INTERNAL; 3019 throw new IllegalStateException("we are not in an error state, " + syncResult); 3020 } 3021 installHandleTooManyDeletesNotification(Account account, String authority, long numDeletes, int userId)3022 private void installHandleTooManyDeletesNotification(Account account, String authority, 3023 long numDeletes, int userId) { 3024 if (mNotificationMgr == null) return; 3025 3026 final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider( 3027 authority, 0 /* flags */); 3028 if (providerInfo == null) { 3029 return; 3030 } 3031 CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager()); 3032 3033 Intent clickIntent = new Intent(mContext, SyncActivityTooManyDeletes.class); 3034 clickIntent.putExtra("account", account); 3035 clickIntent.putExtra("authority", authority); 3036 clickIntent.putExtra("provider", authorityName.toString()); 3037 clickIntent.putExtra("numDeletes", numDeletes); 3038 3039 if (!isActivityAvailable(clickIntent)) { 3040 Log.w(TAG, "No activity found to handle too many deletes."); 3041 return; 3042 } 3043 3044 UserHandle user = new UserHandle(userId); 3045 final PendingIntent pendingIntent = PendingIntent 3046 .getActivityAsUser(mContext, 0, clickIntent, 3047 PendingIntent.FLAG_CANCEL_CURRENT, null, user); 3048 3049 CharSequence tooManyDeletesDescFormat = mContext.getResources().getText( 3050 R.string.contentServiceTooManyDeletesNotificationDesc); 3051 3052 Context contextForUser = getContextForUser(user); 3053 Notification notification = new Notification.Builder(contextForUser) 3054 .setSmallIcon(R.drawable.stat_notify_sync_error) 3055 .setTicker(mContext.getString(R.string.contentServiceSync)) 3056 .setWhen(System.currentTimeMillis()) 3057 .setColor(contextForUser.getColor( 3058 com.android.internal.R.color.system_notification_accent_color)) 3059 .setContentTitle(contextForUser.getString( 3060 R.string.contentServiceSyncNotificationTitle)) 3061 .setContentText( 3062 String.format(tooManyDeletesDescFormat.toString(), authorityName)) 3063 .setContentIntent(pendingIntent) 3064 .build(); 3065 notification.flags |= Notification.FLAG_ONGOING_EVENT; 3066 mNotificationMgr.notifyAsUser(null, account.hashCode() ^ authority.hashCode(), 3067 notification, user); 3068 } 3069 3070 /** 3071 * Checks whether an activity exists on the system image for the given intent. 3072 * 3073 * @param intent The intent for an activity. 3074 * @return Whether or not an activity exists. 3075 */ isActivityAvailable(Intent intent)3076 private boolean isActivityAvailable(Intent intent) { 3077 PackageManager pm = mContext.getPackageManager(); 3078 List<ResolveInfo> list = pm.queryIntentActivities(intent, 0); 3079 int listSize = list.size(); 3080 for (int i = 0; i < listSize; i++) { 3081 ResolveInfo resolveInfo = list.get(i); 3082 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 3083 != 0) { 3084 return true; 3085 } 3086 } 3087 3088 return false; 3089 } 3090 insertStartSyncEvent(SyncOperation syncOperation)3091 public long insertStartSyncEvent(SyncOperation syncOperation) { 3092 final long now = System.currentTimeMillis(); 3093 EventLog.writeEvent(2720, 3094 syncOperation.toEventLog(SyncStorageEngine.EVENT_START)); 3095 return mSyncStorageEngine.insertStartSyncEvent(syncOperation, now); 3096 } 3097 stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, int upstreamActivity, int downstreamActivity, long elapsedTime)3098 public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, 3099 int upstreamActivity, int downstreamActivity, long elapsedTime) { 3100 EventLog.writeEvent(2720, 3101 syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP)); 3102 mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime, 3103 resultMessage, downstreamActivity, upstreamActivity); 3104 } 3105 } 3106 isSyncStillActiveH(ActiveSyncContext activeSyncContext)3107 private boolean isSyncStillActiveH(ActiveSyncContext activeSyncContext) { 3108 for (ActiveSyncContext sync : mActiveSyncContexts) { 3109 if (sync == activeSyncContext) { 3110 return true; 3111 } 3112 } 3113 return false; 3114 } 3115 3116 /** 3117 * Sync extra comparison function. 3118 * @param b1 bundle to compare 3119 * @param b2 other bundle to compare 3120 * @param includeSyncSettings if false, ignore system settings in bundle. 3121 */ syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSyncSettings)3122 public static boolean syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSyncSettings) { 3123 if (b1 == b2) { 3124 return true; 3125 } 3126 // Exit early if we can. 3127 if (includeSyncSettings && b1.size() != b2.size()) { 3128 return false; 3129 } 3130 Bundle bigger = b1.size() > b2.size() ? b1 : b2; 3131 Bundle smaller = b1.size() > b2.size() ? b2 : b1; 3132 for (String key : bigger.keySet()) { 3133 if (!includeSyncSettings && isSyncSetting(key)) { 3134 continue; 3135 } 3136 if (!smaller.containsKey(key)) { 3137 return false; 3138 } 3139 if (!Objects.equals(bigger.get(key), smaller.get(key))) { 3140 return false; 3141 } 3142 } 3143 return true; 3144 } 3145 3146 /** 3147 * @return true if the provided key is used by the SyncManager in scheduling the sync. 3148 */ isSyncSetting(String key)3149 private static boolean isSyncSetting(String key) { 3150 if (key.equals(ContentResolver.SYNC_EXTRAS_EXPEDITED)) { 3151 return true; 3152 } 3153 if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS)) { 3154 return true; 3155 } 3156 if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF)) { 3157 return true; 3158 } 3159 if (key.equals(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY)) { 3160 return true; 3161 } 3162 if (key.equals(ContentResolver.SYNC_EXTRAS_MANUAL)) { 3163 return true; 3164 } 3165 if (key.equals(ContentResolver.SYNC_EXTRAS_UPLOAD)) { 3166 return true; 3167 } 3168 if (key.equals(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS)) { 3169 return true; 3170 } 3171 if (key.equals(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS)) { 3172 return true; 3173 } 3174 if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD)) { 3175 return true; 3176 } 3177 if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD)) { 3178 return true; 3179 } 3180 if (key.equals(ContentResolver.SYNC_EXTRAS_PRIORITY)) { 3181 return true; 3182 } 3183 if (key.equals(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED)) { 3184 return true; 3185 } 3186 if (key.equals(ContentResolver.SYNC_EXTRAS_INITIALIZE)) { 3187 return true; 3188 } 3189 return false; 3190 } 3191 3192 static class PrintTable { 3193 private ArrayList<Object[]> mTable = Lists.newArrayList(); 3194 private final int mCols; 3195 PrintTable(int cols)3196 PrintTable(int cols) { 3197 mCols = cols; 3198 } 3199 set(int row, int col, Object... values)3200 void set(int row, int col, Object... values) { 3201 if (col + values.length > mCols) { 3202 throw new IndexOutOfBoundsException("Table only has " + mCols + 3203 " columns. can't set " + values.length + " at column " + col); 3204 } 3205 for (int i = mTable.size(); i <= row; i++) { 3206 final Object[] list = new Object[mCols]; 3207 mTable.add(list); 3208 for (int j = 0; j < mCols; j++) { 3209 list[j] = ""; 3210 } 3211 } 3212 System.arraycopy(values, 0, mTable.get(row), col, values.length); 3213 } 3214 writeTo(PrintWriter out)3215 void writeTo(PrintWriter out) { 3216 final String[] formats = new String[mCols]; 3217 int totalLength = 0; 3218 for (int col = 0; col < mCols; ++col) { 3219 int maxLength = 0; 3220 for (Object[] row : mTable) { 3221 final int length = row[col].toString().length(); 3222 if (length > maxLength) { 3223 maxLength = length; 3224 } 3225 } 3226 totalLength += maxLength; 3227 formats[col] = String.format("%%-%ds", maxLength); 3228 } 3229 formats[mCols - 1] = "%s"; 3230 printRow(out, formats, mTable.get(0)); 3231 totalLength += (mCols - 1) * 2; 3232 for (int i = 0; i < totalLength; ++i) { 3233 out.print("-"); 3234 } 3235 out.println(); 3236 for (int i = 1, mTableSize = mTable.size(); i < mTableSize; i++) { 3237 Object[] row = mTable.get(i); 3238 printRow(out, formats, row); 3239 } 3240 } 3241 printRow(PrintWriter out, String[] formats, Object[] row)3242 private void printRow(PrintWriter out, String[] formats, Object[] row) { 3243 for (int j = 0, rowLength = row.length; j < rowLength; j++) { 3244 out.printf(String.format(formats[j], row[j].toString())); 3245 out.print(" "); 3246 } 3247 out.println(); 3248 } 3249 getNumRows()3250 public int getNumRows() { 3251 return mTable.size(); 3252 } 3253 } 3254 getContextForUser(UserHandle user)3255 private Context getContextForUser(UserHandle user) { 3256 try { 3257 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user); 3258 } catch (NameNotFoundException e) { 3259 // Default to mContext, not finding the package system is running as is unlikely. 3260 return mContext; 3261 } 3262 } 3263 }