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