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