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