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