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