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