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