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