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