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.TimeMigrationUtils; 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 return TimeMigrationUtils.formatMillisWithFixedFormat(time); 1991 } 1992 1993 private final static Comparator<SyncOperation> sOpDumpComparator = (op1, op2) -> { 1994 int res = Integer.compare(op1.target.userId, op2.target.userId); 1995 if (res != 0) return res; 1996 1997 final Comparator<String> stringComparator = String.CASE_INSENSITIVE_ORDER; 1998 1999 res = stringComparator.compare(op1.target.account.type, op2.target.account.type); 2000 if (res != 0) return res; 2001 2002 res = stringComparator.compare(op1.target.account.name, op2.target.account.name); 2003 if (res != 0) return res; 2004 2005 res = stringComparator.compare(op1.target.provider, op2.target.provider); 2006 if (res != 0) return res; 2007 2008 res = Integer.compare(op1.reason, op2.reason); 2009 if (res != 0) return res; 2010 2011 res = Long.compare(op1.periodMillis, op2.periodMillis); 2012 if (res != 0) return res; 2013 2014 res = Long.compare(op1.expectedRuntime, op2.expectedRuntime); 2015 if (res != 0) return res; 2016 2017 res = Long.compare(op1.jobId, op2.jobId); 2018 if (res != 0) return res; 2019 2020 return 0; 2021 }; 2022 2023 private final static Comparator<SyncOperation> sOpRuntimeComparator = (op1, op2) -> { 2024 int res = Long.compare(op1.expectedRuntime, op2.expectedRuntime); 2025 if (res != 0) return res; 2026 2027 return sOpDumpComparator.compare(op1, op2); 2028 }; 2029 countIf(Collection<T> col, Predicate<T> p)2030 private static <T> int countIf(Collection<T> col, Predicate<T> p) { 2031 int ret = 0; 2032 for (T item : col) { 2033 if (p.test(item)) ret++; 2034 } 2035 return ret; 2036 } 2037 dumpPendingSyncs(PrintWriter pw, SyncAdapterStateFetcher buckets)2038 protected void dumpPendingSyncs(PrintWriter pw, SyncAdapterStateFetcher buckets) { 2039 List<SyncOperation> pendingSyncs = getAllPendingSyncs(); 2040 2041 pw.print("Pending Syncs: "); 2042 pw.println(countIf(pendingSyncs, op -> !op.isPeriodic)); 2043 2044 Collections.sort(pendingSyncs, sOpRuntimeComparator); 2045 int count = 0; 2046 for (SyncOperation op: pendingSyncs) { 2047 if (!op.isPeriodic) { 2048 pw.println(op.dump(null, false, buckets, /*logSafe=*/ false)); 2049 count++; 2050 } 2051 } 2052 pw.println(); 2053 } 2054 dumpPeriodicSyncs(PrintWriter pw, SyncAdapterStateFetcher buckets)2055 protected void dumpPeriodicSyncs(PrintWriter pw, SyncAdapterStateFetcher buckets) { 2056 List<SyncOperation> pendingSyncs = getAllPendingSyncs(); 2057 2058 pw.print("Periodic Syncs: "); 2059 pw.println(countIf(pendingSyncs, op -> op.isPeriodic)); 2060 2061 Collections.sort(pendingSyncs, sOpDumpComparator); 2062 int count = 0; 2063 for (SyncOperation op: pendingSyncs) { 2064 if (op.isPeriodic) { 2065 pw.println(op.dump(null, false, buckets, /*logSafe=*/ false)); 2066 count++; 2067 } 2068 } 2069 pw.println(); 2070 } 2071 2072 /** 2073 * Similar to {@link android.util.TimeUtils#formatDuration}, but it's more suitable and concise 2074 * for the sync manager dumpsys. (Don't add the leading + sign, don't show milliseconds.) 2075 */ formatDurationHMS(StringBuilder sb, long duration)2076 public static StringBuilder formatDurationHMS(StringBuilder sb, long duration) { 2077 duration /= 1000; 2078 if (duration < 0) { 2079 sb.append('-'); 2080 duration = -duration; 2081 } 2082 final long seconds = duration % 60; 2083 duration /= 60; 2084 2085 final long minutes = duration % 60; 2086 duration /= 60; 2087 2088 final long hours = duration % 24; 2089 duration /= 24; 2090 2091 final long days = duration; 2092 2093 boolean print = false; 2094 if (days > 0) { 2095 sb.append(days); 2096 sb.append('d'); 2097 print = true; 2098 } 2099 print = printTwoDigitNumber(sb, hours, 'h', print); 2100 print = printTwoDigitNumber(sb, minutes, 'm', print); 2101 print = printTwoDigitNumber(sb, seconds, 's', print); 2102 if (!print) { 2103 sb.append("0s"); 2104 } 2105 2106 return sb; 2107 } 2108 printTwoDigitNumber(StringBuilder sb, long value, char unit, boolean always)2109 private static boolean printTwoDigitNumber(StringBuilder sb, long value, char unit, 2110 boolean always) { 2111 if (!always && (value == 0)) { 2112 return false; 2113 } 2114 if (always && (value < 10)) { 2115 sb.append('0'); 2116 } 2117 sb.append(value); 2118 sb.append(unit); 2119 return true; 2120 } 2121 dumpSyncState(PrintWriter pw, SyncAdapterStateFetcher buckets)2122 protected void dumpSyncState(PrintWriter pw, SyncAdapterStateFetcher buckets) { 2123 final StringBuilder sb = new StringBuilder(); 2124 2125 pw.print("Data connected: "); pw.println(mDataConnectionIsConnected); 2126 pw.print("Battery saver: "); 2127 pw.println((mPowerManager != null) && mPowerManager.isPowerSaveMode()); 2128 2129 pw.print("Background network restriction: "); 2130 { 2131 final ConnectivityManager cm = getConnectivityManager(); 2132 final int status = (cm == null) ? -1 : cm.getRestrictBackgroundStatus(); 2133 switch (status) { 2134 case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED: 2135 pw.println(" disabled"); 2136 break; 2137 case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED: 2138 pw.println(" whitelisted"); 2139 break; 2140 case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED: 2141 pw.println(" enabled"); 2142 break; 2143 default: 2144 pw.print("Unknown("); 2145 pw.print(status); 2146 pw.println(")"); 2147 break; 2148 } 2149 } 2150 2151 pw.print("Auto sync: "); 2152 List<UserInfo> users = getAllUsers(); 2153 if (users != null) { 2154 for (UserInfo user : users) { 2155 pw.print("u" + user.id + "=" 2156 + mSyncStorageEngine.getMasterSyncAutomatically(user.id) + " "); 2157 } 2158 pw.println(); 2159 } 2160 pw.print("Memory low: "); pw.println(mStorageIsLow); 2161 pw.print("Device idle: "); pw.println(mDeviceIsIdle); 2162 pw.print("Reported active: "); pw.println(mReportedSyncActive); 2163 pw.print("Clock valid: "); pw.println(mSyncStorageEngine.isClockValid()); 2164 2165 final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts(); 2166 2167 pw.print("Accounts: "); 2168 if (accounts != INITIAL_ACCOUNTS_ARRAY) { 2169 pw.println(accounts.length); 2170 } else { 2171 pw.println("not known yet"); 2172 } 2173 final long now = SystemClock.elapsedRealtime(); 2174 pw.print("Now: "); pw.print(now); 2175 pw.println(" (" + formatTime(System.currentTimeMillis()) + ")"); 2176 2177 sb.setLength(0); 2178 pw.print("Uptime: "); pw.print(formatDurationHMS(sb, now)); 2179 pw.println(); 2180 pw.print("Time spent syncing: "); 2181 2182 sb.setLength(0); 2183 pw.print(formatDurationHMS(sb, 2184 mSyncHandler.mSyncTimeTracker.timeSpentSyncing())); 2185 pw.print(", sync "); 2186 pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not "); 2187 pw.println("in progress"); 2188 2189 pw.println(); 2190 pw.println("Active Syncs: " + mActiveSyncContexts.size()); 2191 final PackageManager pm = mContext.getPackageManager(); 2192 for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) { 2193 final long durationInSeconds = (now - activeSyncContext.mStartTime); 2194 pw.print(" "); 2195 sb.setLength(0); 2196 pw.print(formatDurationHMS(sb, durationInSeconds)); 2197 pw.print(" - "); 2198 pw.print(activeSyncContext.mSyncOperation.dump(pm, false, buckets, /*logSafe=*/ false)); 2199 pw.println(); 2200 } 2201 pw.println(); 2202 2203 dumpPendingSyncs(pw, buckets); 2204 dumpPeriodicSyncs(pw, buckets); 2205 2206 // Join the installed sync adapter with the accounts list and emit for everything. 2207 pw.println("Sync Status"); 2208 2209 final ArrayList<Pair<EndPoint, SyncStatusInfo>> statuses = new ArrayList<>(); 2210 2211 mSyncStorageEngine.resetTodayStats(/* force=*/ false); 2212 2213 for (AccountAndUser account : accounts) { 2214 final boolean unlocked; 2215 synchronized (mUnlockedUsers) { 2216 unlocked = mUnlockedUsers.get(account.userId); 2217 } 2218 pw.printf("Account %s u%d %s%s\n", 2219 account.account.name, account.userId, account.account.type, 2220 (unlocked ? "" : " (locked)")); 2221 2222 pw.println("======================================================================="); 2223 final PrintTable table = new PrintTable(16); 2224 table.set(0, 0, 2225 "Authority", // 0 2226 "Syncable", // 1 2227 "Enabled", // 2 2228 2229 "Stats", // 3 "Total", "Today" or "Yesterday". 2230 2231 "Loc", // 4 # of syncs with local sources. (including failures/cancels. ) 2232 "Poll", // 5 "poll" syncs. 2233 "Per", // 6 Periodic syncs. 2234 "Feed", // 7 Syncs with a "feed" extra. (subscribedfeeds?) 2235 "User", // 8 User-initiated 2236 "Othr", // 9 Other sources. 2237 2238 "Tot", // 10 Total syncs (including failures / cancels) 2239 "Fail", // 11 (Failure) 2240 "Can", // 12 (Cancel) 2241 2242 "Time", // 13 Total time 2243 "Last Sync", // 14 2244 "Backoff" // 15 2245 ); 2246 2247 final List<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> sorted = 2248 Lists.newArrayList(); 2249 sorted.addAll(mSyncAdapters.getAllServices(account.userId)); 2250 Collections.sort(sorted, 2251 new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() { 2252 @Override 2253 public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs, 2254 RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) { 2255 return lhs.type.authority.compareTo(rhs.type.authority); 2256 } 2257 }); 2258 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : sorted) { 2259 if (!syncAdapterType.type.accountType.equals(account.account.type)) { 2260 continue; 2261 } 2262 int row = table.getNumRows(); 2263 Pair<AuthorityInfo, SyncStatusInfo> syncAuthoritySyncStatus = 2264 mSyncStorageEngine.getCopyOfAuthorityWithSyncStatus( 2265 new SyncStorageEngine.EndPoint( 2266 account.account, 2267 syncAdapterType.type.authority, 2268 account.userId)); 2269 SyncStorageEngine.AuthorityInfo settings = syncAuthoritySyncStatus.first; 2270 SyncStatusInfo status = syncAuthoritySyncStatus.second; 2271 statuses.add(Pair.create(settings.target, status)); 2272 String authority = settings.target.provider; 2273 if (authority.length() > 50) { 2274 authority = authority.substring(authority.length() - 50); 2275 } 2276 table.set(row, 0, authority, settings.syncable, settings.enabled); 2277 2278 QuadConsumer<String, Stats, Function<Integer, String>, Integer> c = 2279 (label, stats, filter, r) -> { 2280 sb.setLength(0); 2281 table.set(r, 3, 2282 label, 2283 filter.apply(stats.numSourceLocal), 2284 filter.apply(stats.numSourcePoll), 2285 filter.apply(stats.numSourcePeriodic), 2286 filter.apply(stats.numSourceFeed), 2287 filter.apply(stats.numSourceUser), 2288 filter.apply(stats.numSourceOther), 2289 filter.apply(stats.numSyncs), 2290 filter.apply(stats.numFailures), 2291 filter.apply(stats.numCancels), 2292 formatDurationHMS(sb, stats.totalElapsedTime)); 2293 }; 2294 c.accept("Total", status.totalStats, (i) -> Integer.toString(i), row); 2295 c.accept("Today", status.todayStats, this::zeroToEmpty, row + 1); 2296 c.accept("Yestr", status.yesterdayStats, this::zeroToEmpty, row + 2); 2297 2298 final int LAST_SYNC = 14; 2299 final int BACKOFF = LAST_SYNC + 1; 2300 2301 int row1 = row; 2302 if (settings.delayUntil > now) { 2303 table.set(row1++, BACKOFF, "D: " + (settings.delayUntil - now) / 1000); 2304 if (settings.backoffTime > now) { 2305 table.set(row1++, BACKOFF, "B: " + (settings.backoffTime - now) / 1000); 2306 table.set(row1++, BACKOFF, settings.backoffDelay / 1000); 2307 } 2308 } 2309 2310 row1 = row; 2311 if (status.lastSuccessTime != 0) { 2312 table.set(row1++, LAST_SYNC, SyncStorageEngine.SOURCES[status.lastSuccessSource] 2313 + " " + "SUCCESS"); 2314 table.set(row1++, LAST_SYNC, formatTime(status.lastSuccessTime)); 2315 } 2316 if (status.lastFailureTime != 0) { 2317 table.set(row1++, LAST_SYNC, SyncStorageEngine.SOURCES[status.lastFailureSource] 2318 + " " + "FAILURE"); 2319 table.set(row1++, LAST_SYNC, formatTime(status.lastFailureTime)); 2320 //noinspection UnusedAssignment 2321 table.set(row1++, LAST_SYNC, status.lastFailureMesg); 2322 } 2323 } 2324 table.writeTo(pw); 2325 } 2326 2327 dumpSyncHistory(pw); 2328 2329 pw.println(); 2330 pw.println("Per Adapter History"); 2331 pw.println("(SERVER is now split up to FEED and OTHER)"); 2332 2333 for (int i = 0; i < statuses.size(); i++) { 2334 final Pair<EndPoint, SyncStatusInfo> event = statuses.get(i); 2335 2336 pw.print(" "); 2337 pw.print(event.first.account.name); 2338 pw.print('/'); 2339 pw.print(event.first.account.type); 2340 pw.print(" u"); 2341 pw.print(event.first.userId); 2342 pw.print(" ["); 2343 pw.print(event.first.provider); 2344 pw.print("]"); 2345 pw.println(); 2346 2347 pw.println(" Per source last syncs:"); 2348 for (int j = 0; j < SyncStorageEngine.SOURCES.length; j++) { 2349 pw.print(" "); 2350 pw.print(String.format("%8s", SyncStorageEngine.SOURCES[j])); 2351 pw.print(" Success: "); 2352 pw.print(formatTime(event.second.perSourceLastSuccessTimes[j])); 2353 2354 pw.print(" Failure: "); 2355 pw.println(formatTime(event.second.perSourceLastFailureTimes[j])); 2356 } 2357 2358 pw.println(" Last syncs:"); 2359 for (int j = 0; j < event.second.getEventCount(); j++) { 2360 pw.print(" "); 2361 pw.print(formatTime(event.second.getEventTime(j))); 2362 pw.print(' '); 2363 pw.print(event.second.getEvent(j)); 2364 pw.println(); 2365 } 2366 if (event.second.getEventCount() == 0) { 2367 pw.println(" N/A"); 2368 } 2369 } 2370 } 2371 zeroToEmpty(int value)2372 private String zeroToEmpty(int value) { 2373 return (value != 0) ? Integer.toString(value) : ""; 2374 } 2375 dumpTimeSec(PrintWriter pw, long time)2376 private void dumpTimeSec(PrintWriter pw, long time) { 2377 pw.print(time/1000); pw.print('.'); pw.print((time/100)%10); 2378 pw.print('s'); 2379 } 2380 dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds)2381 private void dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds) { 2382 pw.print("Success ("); pw.print(ds.successCount); 2383 if (ds.successCount > 0) { 2384 pw.print(" for "); dumpTimeSec(pw, ds.successTime); 2385 pw.print(" avg="); dumpTimeSec(pw, ds.successTime/ds.successCount); 2386 } 2387 pw.print(") Failure ("); pw.print(ds.failureCount); 2388 if (ds.failureCount > 0) { 2389 pw.print(" for "); dumpTimeSec(pw, ds.failureTime); 2390 pw.print(" avg="); dumpTimeSec(pw, ds.failureTime/ds.failureCount); 2391 } 2392 pw.println(")"); 2393 } 2394 dumpSyncHistory(PrintWriter pw)2395 protected void dumpSyncHistory(PrintWriter pw) { 2396 dumpRecentHistory(pw); 2397 dumpDayStatistics(pw); 2398 } 2399 dumpRecentHistory(PrintWriter pw)2400 private void dumpRecentHistory(PrintWriter pw) { 2401 final ArrayList<SyncStorageEngine.SyncHistoryItem> items 2402 = mSyncStorageEngine.getSyncHistory(); 2403 if (items != null && items.size() > 0) { 2404 final Map<String, AuthoritySyncStats> authorityMap = Maps.newHashMap(); 2405 long totalElapsedTime = 0; 2406 long totalTimes = 0; 2407 final int N = items.size(); 2408 2409 int maxAuthority = 0; 2410 int maxAccount = 0; 2411 for (SyncStorageEngine.SyncHistoryItem item : items) { 2412 SyncStorageEngine.AuthorityInfo authorityInfo 2413 = mSyncStorageEngine.getAuthority(item.authorityId); 2414 final String authorityName; 2415 final String accountKey; 2416 if (authorityInfo != null) { 2417 authorityName = authorityInfo.target.provider; 2418 accountKey = authorityInfo.target.account.name + "/" 2419 + authorityInfo.target.account.type 2420 + " u" + authorityInfo.target.userId; 2421 } else { 2422 authorityName = "Unknown"; 2423 accountKey = "Unknown"; 2424 } 2425 2426 int length = authorityName.length(); 2427 if (length > maxAuthority) { 2428 maxAuthority = length; 2429 } 2430 length = accountKey.length(); 2431 if (length > maxAccount) { 2432 maxAccount = length; 2433 } 2434 2435 final long elapsedTime = item.elapsedTime; 2436 totalElapsedTime += elapsedTime; 2437 totalTimes++; 2438 AuthoritySyncStats authoritySyncStats = authorityMap.get(authorityName); 2439 if (authoritySyncStats == null) { 2440 authoritySyncStats = new AuthoritySyncStats(authorityName); 2441 authorityMap.put(authorityName, authoritySyncStats); 2442 } 2443 authoritySyncStats.elapsedTime += elapsedTime; 2444 authoritySyncStats.times++; 2445 final Map<String, AccountSyncStats> accountMap = authoritySyncStats.accountMap; 2446 AccountSyncStats accountSyncStats = accountMap.get(accountKey); 2447 if (accountSyncStats == null) { 2448 accountSyncStats = new AccountSyncStats(accountKey); 2449 accountMap.put(accountKey, accountSyncStats); 2450 } 2451 accountSyncStats.elapsedTime += elapsedTime; 2452 accountSyncStats.times++; 2453 2454 } 2455 2456 if (totalElapsedTime > 0) { 2457 pw.println(); 2458 pw.printf("Detailed Statistics (Recent history): " 2459 + "%d (# of times) %ds (sync time)\n", 2460 totalTimes, totalElapsedTime / 1000); 2461 2462 final List<AuthoritySyncStats> sortedAuthorities = 2463 new ArrayList<AuthoritySyncStats>(authorityMap.values()); 2464 Collections.sort(sortedAuthorities, new Comparator<AuthoritySyncStats>() { 2465 @Override 2466 public int compare(AuthoritySyncStats lhs, AuthoritySyncStats rhs) { 2467 // reverse order 2468 int compare = Integer.compare(rhs.times, lhs.times); 2469 if (compare == 0) { 2470 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime); 2471 } 2472 return compare; 2473 } 2474 }); 2475 2476 final int maxLength = Math.max(maxAuthority, maxAccount + 3); 2477 final int padLength = 2 + 2 + maxLength + 2 + 10 + 11; 2478 final char chars[] = new char[padLength]; 2479 Arrays.fill(chars, '-'); 2480 final String separator = new String(chars); 2481 2482 final String authorityFormat = 2483 String.format(" %%-%ds: %%-9s %%-11s\n", maxLength + 2); 2484 final String accountFormat = 2485 String.format(" %%-%ds: %%-9s %%-11s\n", maxLength); 2486 2487 pw.println(separator); 2488 for (AuthoritySyncStats authoritySyncStats : sortedAuthorities) { 2489 String name = authoritySyncStats.name; 2490 long elapsedTime; 2491 int times; 2492 String timeStr; 2493 String timesStr; 2494 2495 elapsedTime = authoritySyncStats.elapsedTime; 2496 times = authoritySyncStats.times; 2497 timeStr = String.format("%ds/%d%%", 2498 elapsedTime / 1000, 2499 elapsedTime * 100 / totalElapsedTime); 2500 timesStr = String.format("%d/%d%%", 2501 times, 2502 times * 100 / totalTimes); 2503 pw.printf(authorityFormat, name, timesStr, timeStr); 2504 2505 final List<AccountSyncStats> sortedAccounts = 2506 new ArrayList<AccountSyncStats>( 2507 authoritySyncStats.accountMap.values()); 2508 Collections.sort(sortedAccounts, new Comparator<AccountSyncStats>() { 2509 @Override 2510 public int compare(AccountSyncStats lhs, AccountSyncStats rhs) { 2511 // reverse order 2512 int compare = Integer.compare(rhs.times, lhs.times); 2513 if (compare == 0) { 2514 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime); 2515 } 2516 return compare; 2517 } 2518 }); 2519 for (AccountSyncStats stats: sortedAccounts) { 2520 elapsedTime = stats.elapsedTime; 2521 times = stats.times; 2522 timeStr = String.format("%ds/%d%%", 2523 elapsedTime / 1000, 2524 elapsedTime * 100 / totalElapsedTime); 2525 timesStr = String.format("%d/%d%%", 2526 times, 2527 times * 100 / totalTimes); 2528 pw.printf(accountFormat, stats.name, timesStr, timeStr); 2529 } 2530 pw.println(separator); 2531 } 2532 } 2533 2534 pw.println(); 2535 pw.println("Recent Sync History"); 2536 pw.println("(SERVER is now split up to FEED and OTHER)"); 2537 final String format = " %-" + maxAccount + "s %-" + maxAuthority + "s %s\n"; 2538 final Map<String, Long> lastTimeMap = Maps.newHashMap(); 2539 final PackageManager pm = mContext.getPackageManager(); 2540 for (int i = 0; i < N; i++) { 2541 SyncStorageEngine.SyncHistoryItem item = items.get(i); 2542 SyncStorageEngine.AuthorityInfo authorityInfo 2543 = mSyncStorageEngine.getAuthority(item.authorityId); 2544 final String authorityName; 2545 final String accountKey; 2546 if (authorityInfo != null) { 2547 authorityName = authorityInfo.target.provider; 2548 accountKey = authorityInfo.target.account.name + "/" 2549 + authorityInfo.target.account.type 2550 + " u" + authorityInfo.target.userId; 2551 } else { 2552 authorityName = "Unknown"; 2553 accountKey = "Unknown"; 2554 } 2555 final long elapsedTime = item.elapsedTime; 2556 final long eventTime = item.eventTime; 2557 2558 final String key = authorityName + "/" + accountKey; 2559 final Long lastEventTime = lastTimeMap.get(key); 2560 final String diffString; 2561 if (lastEventTime == null) { 2562 diffString = ""; 2563 } else { 2564 final long diff = (lastEventTime - eventTime) / 1000; 2565 if (diff < 60) { 2566 diffString = String.valueOf(diff); 2567 } else if (diff < 3600) { 2568 diffString = String.format("%02d:%02d", diff / 60, diff % 60); 2569 } else { 2570 final long sec = diff % 3600; 2571 diffString = String.format("%02d:%02d:%02d", 2572 diff / 3600, sec / 60, sec % 60); 2573 } 2574 } 2575 lastTimeMap.put(key, eventTime); 2576 2577 pw.printf(" #%-3d: %s %8s %5.1fs %8s", 2578 i + 1, 2579 formatTime(eventTime), 2580 SyncStorageEngine.SOURCES[item.source], 2581 ((float) elapsedTime) / 1000, 2582 diffString); 2583 pw.printf(format, accountKey, authorityName, 2584 SyncOperation.reasonToString(pm, item.reason)); 2585 2586 if (item.event != SyncStorageEngine.EVENT_STOP 2587 || item.upstreamActivity != 0 2588 || item.downstreamActivity != 0) { 2589 pw.printf(" event=%d upstreamActivity=%d downstreamActivity=%d\n", 2590 item.event, 2591 item.upstreamActivity, 2592 item.downstreamActivity); 2593 } 2594 if (item.mesg != null 2595 && !SyncStorageEngine.MESG_SUCCESS.equals(item.mesg)) { 2596 pw.printf(" mesg=%s\n", item.mesg); 2597 } 2598 } 2599 pw.println(); 2600 pw.println("Recent Sync History Extras"); 2601 pw.println("(SERVER is now split up to FEED and OTHER)"); 2602 for (int i = 0; i < N; i++) { 2603 final SyncStorageEngine.SyncHistoryItem item = items.get(i); 2604 final Bundle extras = item.extras; 2605 if (extras == null || extras.size() == 0) { 2606 continue; 2607 } 2608 final SyncStorageEngine.AuthorityInfo authorityInfo 2609 = mSyncStorageEngine.getAuthority(item.authorityId); 2610 final String authorityName; 2611 final String accountKey; 2612 if (authorityInfo != null) { 2613 authorityName = authorityInfo.target.provider; 2614 accountKey = authorityInfo.target.account.name + "/" 2615 + authorityInfo.target.account.type 2616 + " u" + authorityInfo.target.userId; 2617 } else { 2618 authorityName = "Unknown"; 2619 accountKey = "Unknown"; 2620 } 2621 final long eventTime = item.eventTime; 2622 2623 pw.printf(" #%-3d: %s %8s ", 2624 i + 1, 2625 formatTime(eventTime), 2626 SyncStorageEngine.SOURCES[item.source]); 2627 2628 pw.printf(format, accountKey, authorityName, extras); 2629 } 2630 } 2631 } 2632 dumpDayStatistics(PrintWriter pw)2633 private void dumpDayStatistics(PrintWriter pw) { 2634 SyncStorageEngine.DayStats dses[] = mSyncStorageEngine.getDayStatistics(); 2635 if (dses != null && dses[0] != null) { 2636 pw.println(); 2637 pw.println("Sync Statistics"); 2638 pw.print(" Today: "); dumpDayStatistic(pw, dses[0]); 2639 int today = dses[0].day; 2640 int i; 2641 SyncStorageEngine.DayStats ds; 2642 2643 // Print each day in the current week. 2644 for (i=1; i<=6 && i < dses.length; i++) { 2645 ds = dses[i]; 2646 if (ds == null) break; 2647 int delta = today-ds.day; 2648 if (delta > 6) break; 2649 2650 pw.print(" Day-"); pw.print(delta); pw.print(": "); 2651 dumpDayStatistic(pw, ds); 2652 } 2653 2654 // Aggregate all following days into weeks and print totals. 2655 int weekDay = today; 2656 while (i < dses.length) { 2657 SyncStorageEngine.DayStats aggr = null; 2658 weekDay -= 7; 2659 while (i < dses.length) { 2660 ds = dses[i]; 2661 if (ds == null) { 2662 i = dses.length; 2663 break; 2664 } 2665 int delta = weekDay-ds.day; 2666 if (delta > 6) break; 2667 i++; 2668 2669 if (aggr == null) { 2670 aggr = new SyncStorageEngine.DayStats(weekDay); 2671 } 2672 aggr.successCount += ds.successCount; 2673 aggr.successTime += ds.successTime; 2674 aggr.failureCount += ds.failureCount; 2675 aggr.failureTime += ds.failureTime; 2676 } 2677 if (aggr != null) { 2678 pw.print(" Week-"); pw.print((today-weekDay)/7); pw.print(": "); 2679 dumpDayStatistic(pw, aggr); 2680 } 2681 } 2682 } 2683 } 2684 dumpSyncAdapters(IndentingPrintWriter pw)2685 private void dumpSyncAdapters(IndentingPrintWriter pw) { 2686 pw.println(); 2687 final List<UserInfo> users = getAllUsers(); 2688 if (users != null) { 2689 for (UserInfo user : users) { 2690 pw.println("Sync adapters for " + user + ":"); 2691 pw.increaseIndent(); 2692 for (RegisteredServicesCache.ServiceInfo<?> info : 2693 mSyncAdapters.getAllServices(user.id)) { 2694 pw.println(info); 2695 } 2696 pw.decreaseIndent(); 2697 pw.println(); 2698 } 2699 } 2700 } 2701 2702 private static class AuthoritySyncStats { 2703 String name; 2704 long elapsedTime; 2705 int times; 2706 Map<String, AccountSyncStats> accountMap = Maps.newHashMap(); 2707 AuthoritySyncStats(String name)2708 private AuthoritySyncStats(String name) { 2709 this.name = name; 2710 } 2711 } 2712 2713 private static class AccountSyncStats { 2714 String name; 2715 long elapsedTime; 2716 int times; 2717 AccountSyncStats(String name)2718 private AccountSyncStats(String name) { 2719 this.name = name; 2720 } 2721 } 2722 2723 interface OnReadyCallback { onReady()2724 void onReady(); 2725 } 2726 sendOnUnsyncableAccount(@onNull Context context, @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo, @UserIdInt int userId, @NonNull OnReadyCallback onReadyCallback)2727 static void sendOnUnsyncableAccount(@NonNull Context context, 2728 @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo, 2729 @UserIdInt int userId, @NonNull OnReadyCallback onReadyCallback) { 2730 OnUnsyncableAccountCheck connection = new OnUnsyncableAccountCheck(syncAdapterInfo, 2731 onReadyCallback); 2732 2733 boolean isBound = context.bindServiceAsUser( 2734 getAdapterBindIntent(context, syncAdapterInfo.componentName, userId), 2735 connection, SYNC_ADAPTER_CONNECTION_FLAGS, UserHandle.of(userId)); 2736 2737 if (isBound) { 2738 // Unbind after SERVICE_BOUND_TIME_MILLIS to not leak the connection. 2739 (new Handler(Looper.getMainLooper())).postDelayed( 2740 () -> context.unbindService(connection), 2741 OnUnsyncableAccountCheck.SERVICE_BOUND_TIME_MILLIS); 2742 } else { 2743 /* 2744 * The default implementation of adapter.onUnsyncableAccount returns true. Hence if 2745 * there the service cannot be bound, assume the default behavior. 2746 */ 2747 connection.onReady(); 2748 } 2749 } 2750 2751 2752 /** 2753 * Helper class for calling ISyncAdapter.onUnsyncableAccountDone. 2754 * 2755 * If this returns {@code true} the onReadyCallback is called. Otherwise nothing happens. 2756 */ 2757 private static class OnUnsyncableAccountCheck implements ServiceConnection { 2758 static final long SERVICE_BOUND_TIME_MILLIS = 5000; 2759 2760 private final @NonNull OnReadyCallback mOnReadyCallback; 2761 private final @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> 2762 mSyncAdapterInfo; 2763 OnUnsyncableAccountCheck( @onNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo, @NonNull OnReadyCallback onReadyCallback)2764 OnUnsyncableAccountCheck( 2765 @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo, 2766 @NonNull OnReadyCallback onReadyCallback) { 2767 mSyncAdapterInfo = syncAdapterInfo; 2768 mOnReadyCallback = onReadyCallback; 2769 } 2770 onReady()2771 private void onReady() { 2772 long identity = Binder.clearCallingIdentity(); 2773 try { 2774 mOnReadyCallback.onReady(); 2775 } finally { 2776 Binder.restoreCallingIdentity(identity); 2777 } 2778 } 2779 2780 @Override onServiceConnected(ComponentName name, IBinder service)2781 public void onServiceConnected(ComponentName name, IBinder service) { 2782 final ISyncAdapter adapter = ISyncAdapter.Stub.asInterface(service); 2783 2784 try { 2785 adapter.onUnsyncableAccount(new ISyncAdapterUnsyncableAccountCallback.Stub() { 2786 @Override 2787 public void onUnsyncableAccountDone(boolean isReady) { 2788 if (isReady) { 2789 onReady(); 2790 } 2791 } 2792 }); 2793 } catch (RemoteException e) { 2794 Slog.e(TAG, "Could not call onUnsyncableAccountDone " + mSyncAdapterInfo, e); 2795 /* 2796 * The default implementation of adapter.onUnsyncableAccount returns true. Hence if 2797 * there is a crash in the implementation, assume the default behavior. 2798 */ 2799 onReady(); 2800 } 2801 } 2802 2803 @Override onServiceDisconnected(ComponentName name)2804 public void onServiceDisconnected(ComponentName name) { 2805 // Wait until the service connects again 2806 } 2807 } 2808 2809 /** 2810 * A helper object to keep track of the time we have spent syncing since the last boot 2811 */ 2812 private class SyncTimeTracker { 2813 /** True if a sync was in progress on the most recent call to update() */ 2814 boolean mLastWasSyncing = false; 2815 /** Used to track when lastWasSyncing was last set */ 2816 long mWhenSyncStarted = 0; 2817 /** The cumulative time we have spent syncing */ 2818 private long mTimeSpentSyncing; 2819 2820 /** Call to let the tracker know that the sync state may have changed */ update()2821 public synchronized void update() { 2822 final boolean isSyncInProgress = !mActiveSyncContexts.isEmpty(); 2823 if (isSyncInProgress == mLastWasSyncing) return; 2824 final long now = SystemClock.elapsedRealtime(); 2825 if (isSyncInProgress) { 2826 mWhenSyncStarted = now; 2827 } else { 2828 mTimeSpentSyncing += now - mWhenSyncStarted; 2829 } 2830 mLastWasSyncing = isSyncInProgress; 2831 } 2832 2833 /** Get how long we have been syncing, in ms */ timeSpentSyncing()2834 public synchronized long timeSpentSyncing() { 2835 if (!mLastWasSyncing) return mTimeSpentSyncing; 2836 2837 final long now = SystemClock.elapsedRealtime(); 2838 return mTimeSpentSyncing + (now - mWhenSyncStarted); 2839 } 2840 } 2841 2842 class ServiceConnectionData { 2843 public final ActiveSyncContext activeSyncContext; 2844 public final IBinder adapter; 2845 ServiceConnectionData(ActiveSyncContext activeSyncContext, IBinder adapter)2846 ServiceConnectionData(ActiveSyncContext activeSyncContext, IBinder adapter) { 2847 this.activeSyncContext = activeSyncContext; 2848 this.adapter = adapter; 2849 } 2850 } 2851 2852 @Nullable getInstance()2853 private static SyncManager getInstance() { 2854 synchronized (SyncManager.class) { 2855 if (sInstance == null) { 2856 Slog.wtf(TAG, "sInstance == null"); // Maybe called too early? 2857 } 2858 return sInstance; 2859 } 2860 } 2861 2862 /** 2863 * @return whether the device is ready to run sync jobs for a given user. 2864 */ readyToSync(int userId)2865 public static boolean readyToSync(int userId) { 2866 final SyncManager instance = getInstance(); 2867 return (instance != null) && SyncJobService.isReady() 2868 && instance.mProvisioned && instance.isUserUnlocked(userId); 2869 } 2870 sendMessage(Message message)2871 public static void sendMessage(Message message) { 2872 final SyncManager instance = getInstance(); 2873 if (instance != null) { 2874 instance.mSyncHandler.sendMessage(message); 2875 } 2876 } 2877 2878 /** 2879 * Handles SyncOperation Messages that are posted to the associated 2880 * HandlerThread. 2881 */ 2882 class SyncHandler extends Handler { 2883 // Messages that can be sent on mHandler. 2884 private static final int MESSAGE_SYNC_FINISHED = 1; 2885 private static final int MESSAGE_SERVICE_CONNECTED = 4; 2886 private static final int MESSAGE_SERVICE_DISCONNECTED = 5; 2887 private static final int MESSAGE_CANCEL = 6; 2888 static final int MESSAGE_START_SYNC = 10; 2889 static final int MESSAGE_STOP_SYNC = 11; 2890 static final int MESSAGE_SCHEDULE_SYNC = 12; 2891 static final int MESSAGE_UPDATE_PERIODIC_SYNC = 13; 2892 static final int MESSAGE_REMOVE_PERIODIC_SYNC = 14; 2893 2894 /** 2895 * Posted periodically to monitor network process for long-running syncs. 2896 * obj: {@link com.android.server.content.SyncManager.ActiveSyncContext} 2897 */ 2898 private static final int MESSAGE_MONITOR_SYNC = 8; 2899 private static final int MESSAGE_ACCOUNTS_UPDATED = 9; 2900 2901 public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker(); 2902 private final HashMap<String, PowerManager.WakeLock> mWakeLocks = Maps.newHashMap(); 2903 SyncHandler(Looper looper)2904 public SyncHandler(Looper looper) { 2905 super(looper); 2906 } 2907 handleMessage(Message msg)2908 public void handleMessage(Message msg) { 2909 // TODO Do we really need this wake lock?? If we actually needed it, this is probably 2910 // not the best place to acquire the lock -- it's probably too late, because the device 2911 // could have gone to sleep before we reach here. 2912 mSyncManagerWakeLock.acquire(); 2913 try { 2914 handleSyncMessage(msg); 2915 } finally { 2916 mSyncManagerWakeLock.release(); 2917 } 2918 } 2919 handleSyncMessage(Message msg)2920 private void handleSyncMessage(Message msg) { 2921 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2922 2923 try { 2924 mDataConnectionIsConnected = readDataConnectionState(); 2925 switch (msg.what) { 2926 case MESSAGE_ACCOUNTS_UPDATED: 2927 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2928 Slog.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED"); 2929 } 2930 EndPoint targets = (EndPoint) msg.obj; 2931 updateRunningAccountsH(targets); 2932 break; 2933 case MESSAGE_SCHEDULE_SYNC: 2934 ScheduleSyncMessagePayload syncPayload = 2935 (ScheduleSyncMessagePayload) msg.obj; 2936 SyncOperation op = syncPayload.syncOperation; 2937 scheduleSyncOperationH(op, syncPayload.minDelayMillis); 2938 break; 2939 2940 case MESSAGE_START_SYNC: 2941 op = (SyncOperation) msg.obj; 2942 startSyncH(op); 2943 break; 2944 2945 case MESSAGE_STOP_SYNC: 2946 op = (SyncOperation) msg.obj; 2947 if (isLoggable) { 2948 Slog.v(TAG, "Stop sync received."); 2949 } 2950 ActiveSyncContext asc = findActiveSyncContextH(op.jobId); 2951 if (asc != null) { 2952 runSyncFinishedOrCanceledH(null /* no result */, asc); 2953 boolean reschedule = msg.arg1 != 0; 2954 boolean applyBackoff = msg.arg2 != 0; 2955 if (isLoggable) { 2956 Slog.v(TAG, "Stopping sync. Reschedule: " + reschedule 2957 + "Backoff: " + applyBackoff); 2958 } 2959 if (applyBackoff) { 2960 increaseBackoffSetting(op.target); 2961 } 2962 if (reschedule) { 2963 deferStoppedSyncH(op, 0); 2964 } 2965 } 2966 break; 2967 2968 case MESSAGE_UPDATE_PERIODIC_SYNC: 2969 UpdatePeriodicSyncMessagePayload data = 2970 (UpdatePeriodicSyncMessagePayload) msg.obj; 2971 updateOrAddPeriodicSyncH(data.target, data.pollFrequency, 2972 data.flex, data.extras); 2973 break; 2974 case MESSAGE_REMOVE_PERIODIC_SYNC: 2975 Pair<EndPoint, String> args = (Pair<EndPoint, String>) (msg.obj); 2976 removePeriodicSyncH(args.first, msg.getData(), args.second); 2977 break; 2978 2979 case SyncHandler.MESSAGE_CANCEL: 2980 SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj; 2981 Bundle extras = msg.peekData(); 2982 if (isLoggable) { 2983 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_CANCEL: " 2984 + endpoint + " bundle: " + extras); 2985 } 2986 cancelActiveSyncH(endpoint, extras, "MESSAGE_CANCEL"); 2987 break; 2988 2989 case SyncHandler.MESSAGE_SYNC_FINISHED: 2990 SyncFinishedOrCancelledMessagePayload payload = 2991 (SyncFinishedOrCancelledMessagePayload) msg.obj; 2992 if (!isSyncStillActiveH(payload.activeSyncContext)) { 2993 if (isLoggable) { 2994 Log.d(TAG, "handleSyncHandlerMessage: dropping since the " 2995 + "sync is no longer active: " 2996 + payload.activeSyncContext); 2997 } 2998 break; 2999 } 3000 if (isLoggable) { 3001 Slog.v(TAG, "syncFinished" + payload.activeSyncContext.mSyncOperation); 3002 } 3003 SyncJobService.callJobFinished( 3004 payload.activeSyncContext.mSyncOperation.jobId, false, 3005 "sync finished"); 3006 runSyncFinishedOrCanceledH(payload.syncResult, 3007 payload.activeSyncContext); 3008 break; 3009 3010 case SyncHandler.MESSAGE_SERVICE_CONNECTED: { 3011 ServiceConnectionData msgData = (ServiceConnectionData) msg.obj; 3012 if (isLoggable) { 3013 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: " 3014 + msgData.activeSyncContext); 3015 } 3016 // Check that this isn't an old message. 3017 if (isSyncStillActiveH(msgData.activeSyncContext)) { 3018 runBoundToAdapterH( 3019 msgData.activeSyncContext, 3020 msgData.adapter); 3021 } 3022 break; 3023 } 3024 3025 case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: { 3026 final ActiveSyncContext currentSyncContext = 3027 ((ServiceConnectionData) msg.obj).activeSyncContext; 3028 if (isLoggable) { 3029 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: " 3030 + currentSyncContext); 3031 } 3032 // Check that this isn't an old message. 3033 if (isSyncStillActiveH(currentSyncContext)) { 3034 // cancel the sync if we have a syncadapter, which means one is 3035 // outstanding 3036 try { 3037 if (currentSyncContext.mSyncAdapter != null) { 3038 mLogger.log("Calling cancelSync for SERVICE_DISCONNECTED ", 3039 currentSyncContext, 3040 " adapter=", currentSyncContext.mSyncAdapter); 3041 currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext); 3042 mLogger.log("Canceled"); 3043 } 3044 } catch (RemoteException e) { 3045 mLogger.log("RemoteException ", Log.getStackTraceString(e)); 3046 // We don't need to retry this in this case. 3047 } 3048 3049 // Pretend that the sync failed with an IOException, 3050 // which is a soft error. 3051 SyncResult syncResult = new SyncResult(); 3052 syncResult.stats.numIoExceptions++; 3053 SyncJobService.callJobFinished( 3054 currentSyncContext.mSyncOperation.jobId, false, 3055 "service disconnected"); 3056 runSyncFinishedOrCanceledH(syncResult, currentSyncContext); 3057 } 3058 break; 3059 } 3060 3061 case SyncHandler.MESSAGE_MONITOR_SYNC: 3062 ActiveSyncContext monitoredSyncContext = (ActiveSyncContext) msg.obj; 3063 if (isLoggable) { 3064 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_MONITOR_SYNC: " + 3065 monitoredSyncContext.mSyncOperation.target); 3066 } 3067 3068 if (isSyncNotUsingNetworkH(monitoredSyncContext)) { 3069 Log.w(TAG, String.format( 3070 "Detected sync making no progress for %s. cancelling.", 3071 logSafe(monitoredSyncContext))); 3072 SyncJobService.callJobFinished( 3073 monitoredSyncContext.mSyncOperation.jobId, false, 3074 "no network activity"); 3075 runSyncFinishedOrCanceledH( 3076 null /* cancel => no result */, monitoredSyncContext); 3077 } else { 3078 // Repost message to check again. 3079 postMonitorSyncProgressMessage(monitoredSyncContext); 3080 } 3081 break; 3082 3083 } 3084 } finally { 3085 mSyncTimeTracker.update(); 3086 } 3087 } 3088 getSyncWakeLock(SyncOperation operation)3089 private PowerManager.WakeLock getSyncWakeLock(SyncOperation operation) { 3090 final String wakeLockKey = operation.wakeLockName(); 3091 PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey); 3092 if (wakeLock == null) { 3093 final String name = SYNC_WAKE_LOCK_PREFIX + wakeLockKey; 3094 wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name); 3095 wakeLock.setReferenceCounted(false); 3096 mWakeLocks.put(wakeLockKey, wakeLock); 3097 } 3098 return wakeLock; 3099 } 3100 3101 /** 3102 * Defer the specified SyncOperation by rescheduling it on the JobScheduler with some 3103 * delay. This is equivalent to a failure. If this is a periodic sync, a delayed one-off 3104 * sync will be scheduled. 3105 */ deferSyncH(SyncOperation op, long delay, String why)3106 private void deferSyncH(SyncOperation op, long delay, String why) { 3107 mLogger.log("deferSyncH() ", (op.isPeriodic ? "periodic " : ""), 3108 "sync. op=", op, " delay=", delay, " why=", why); 3109 SyncJobService.callJobFinished(op.jobId, false, why); 3110 if (op.isPeriodic) { 3111 scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay); 3112 } else { 3113 // mSyncJobService.callJobFinished is async, so cancel the job to ensure we don't 3114 // find the this job in the pending jobs list while looking for duplicates 3115 // before scheduling it at a later time. 3116 cancelJob(op, "deferSyncH()"); 3117 scheduleSyncOperationH(op, delay); 3118 } 3119 } 3120 3121 /* Same as deferSyncH, but assumes that job is no longer running on JobScheduler. */ deferStoppedSyncH(SyncOperation op, long delay)3122 private void deferStoppedSyncH(SyncOperation op, long delay) { 3123 if (op.isPeriodic) { 3124 scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay); 3125 } else { 3126 scheduleSyncOperationH(op, delay); 3127 } 3128 } 3129 3130 /** 3131 * Cancel an active sync and reschedule it on the JobScheduler with some delay. 3132 */ deferActiveSyncH(ActiveSyncContext asc, String why)3133 private void deferActiveSyncH(ActiveSyncContext asc, String why) { 3134 SyncOperation op = asc.mSyncOperation; 3135 runSyncFinishedOrCanceledH(null, asc); 3136 deferSyncH(op, SYNC_DELAY_ON_CONFLICT, why); 3137 } 3138 startSyncH(SyncOperation op)3139 private void startSyncH(SyncOperation op) { 3140 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 3141 if (isLoggable) Slog.v(TAG, op.toString()); 3142 3143 // At this point, we know the device has been connected to the server, so 3144 // assume the clock is correct. 3145 mSyncStorageEngine.setClockValid(); 3146 3147 SyncJobService.markSyncStarted(op.jobId); 3148 3149 if (mStorageIsLow) { 3150 deferSyncH(op, SYNC_DELAY_ON_LOW_STORAGE, "storage low"); 3151 return; 3152 } 3153 3154 if (op.isPeriodic) { 3155 // Don't allow this periodic to run if a previous instance failed and is currently 3156 // scheduled according to some backoff criteria. 3157 List<SyncOperation> ops = getAllPendingSyncs(); 3158 for (SyncOperation syncOperation: ops) { 3159 if (syncOperation.sourcePeriodicId == op.jobId) { 3160 SyncJobService.callJobFinished(op.jobId, false, 3161 "periodic sync, pending"); 3162 return; 3163 } 3164 } 3165 // Don't allow this periodic to run if a previous instance failed and is currently 3166 // executing according to some backoff criteria. 3167 for (ActiveSyncContext asc: mActiveSyncContexts) { 3168 if (asc.mSyncOperation.sourcePeriodicId == op.jobId) { 3169 SyncJobService.callJobFinished(op.jobId, false, 3170 "periodic sync, already running"); 3171 return; 3172 } 3173 } 3174 // Check for adapter delays. 3175 if (isAdapterDelayed(op.target)) { 3176 deferSyncH(op, 0 /* No minimum delay */, "backing off"); 3177 return; 3178 } 3179 } 3180 3181 // Check for conflicting syncs. 3182 for (ActiveSyncContext asc: mActiveSyncContexts) { 3183 if (asc.mSyncOperation.isConflict(op)) { 3184 // If the provided SyncOperation conflicts with a running one, the lower 3185 // priority sync is pre-empted. 3186 if (asc.mSyncOperation.findPriority() >= op.findPriority()) { 3187 if (isLoggable) { 3188 Slog.v(TAG, "Rescheduling sync due to conflict " + op.toString()); 3189 } 3190 deferSyncH(op, SYNC_DELAY_ON_CONFLICT, "delay on conflict"); 3191 return; 3192 } else { 3193 if (isLoggable) { 3194 Slog.v(TAG, "Pushing back running sync due to a higher priority sync"); 3195 } 3196 deferActiveSyncH(asc, "preempted"); 3197 break; 3198 } 3199 } 3200 } 3201 3202 final int syncOpState = computeSyncOpState(op); 3203 switch (syncOpState) { 3204 case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS: 3205 case SYNC_OP_STATE_INVALID: { 3206 SyncJobService.callJobFinished(op.jobId, false, 3207 "invalid op state: " + syncOpState); 3208 } return; 3209 } 3210 3211 if (!dispatchSyncOperation(op)) { 3212 SyncJobService.callJobFinished(op.jobId, false, "dispatchSyncOperation() failed"); 3213 } 3214 3215 setAuthorityPendingState(op.target); 3216 } 3217 findActiveSyncContextH(int jobId)3218 private ActiveSyncContext findActiveSyncContextH(int jobId) { 3219 for (ActiveSyncContext asc: mActiveSyncContexts) { 3220 SyncOperation op = asc.mSyncOperation; 3221 if (op != null && op.jobId == jobId) { 3222 return asc; 3223 } 3224 } 3225 return null; 3226 } 3227 updateRunningAccountsH(EndPoint syncTargets)3228 private void updateRunningAccountsH(EndPoint syncTargets) { 3229 AccountAndUser[] oldAccounts = mRunningAccounts; 3230 mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts(); 3231 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3232 Slog.v(TAG, "Accounts list: "); 3233 for (AccountAndUser acc : mRunningAccounts) { 3234 Slog.v(TAG, acc.toString()); 3235 } 3236 } 3237 if (mLogger.enabled()) { 3238 mLogger.log("updateRunningAccountsH: ", Arrays.toString(mRunningAccounts)); 3239 } 3240 removeStaleAccounts(); 3241 3242 AccountAndUser[] accounts = mRunningAccounts; 3243 for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) { 3244 if (!containsAccountAndUser(accounts, 3245 currentSyncContext.mSyncOperation.target.account, 3246 currentSyncContext.mSyncOperation.target.userId)) { 3247 Log.d(TAG, "canceling sync since the account is no longer running"); 3248 sendSyncFinishedOrCanceledMessage(currentSyncContext, 3249 null /* no result since this is a cancel */); 3250 } 3251 } 3252 3253 if (syncTargets != null) { 3254 // On account add, check if there are any settings to be restored. 3255 for (AccountAndUser aau : mRunningAccounts) { 3256 if (!containsAccountAndUser(oldAccounts, aau.account, aau.userId)) { 3257 if (Log.isLoggable(TAG, Log.DEBUG)) { 3258 Log.d(TAG, "Account " + aau.account 3259 + " added, checking sync restore data"); 3260 } 3261 AccountSyncSettingsBackupHelper.accountAdded(mContext, syncTargets.userId); 3262 break; 3263 } 3264 } 3265 } 3266 3267 // Cancel all jobs from non-existent accounts. 3268 AccountAndUser[] allAccounts = AccountManagerService.getSingleton().getAllAccounts(); 3269 List<SyncOperation> ops = getAllPendingSyncs(); 3270 for (SyncOperation op: ops) { 3271 if (!containsAccountAndUser(allAccounts, op.target.account, op.target.userId)) { 3272 mLogger.log("canceling: ", op); 3273 cancelJob(op, "updateRunningAccountsH()"); 3274 } 3275 } 3276 3277 if (syncTargets != null) { 3278 scheduleSync(syncTargets.account, syncTargets.userId, 3279 SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider, 3280 null, AuthorityInfo.NOT_INITIALIZED, 3281 ContentResolver.SYNC_EXEMPTION_NONE, Process.myUid(), -4, null); 3282 } 3283 } 3284 3285 /** 3286 * The given SyncOperation will be removed and a new one scheduled in its place if 3287 * an updated period or flex is specified. 3288 * @param syncOperation SyncOperation whose period and flex is to be updated. 3289 * @param pollFrequencyMillis new period in milliseconds. 3290 * @param flexMillis new flex time in milliseconds. 3291 */ maybeUpdateSyncPeriodH(SyncOperation syncOperation, long pollFrequencyMillis, long flexMillis)3292 private void maybeUpdateSyncPeriodH(SyncOperation syncOperation, long pollFrequencyMillis, 3293 long flexMillis) { 3294 if (!(pollFrequencyMillis == syncOperation.periodMillis 3295 && flexMillis == syncOperation.flexMillis)) { 3296 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3297 Slog.v(TAG, "updating period " + syncOperation + " to " + pollFrequencyMillis 3298 + " and flex to " + flexMillis); 3299 } 3300 SyncOperation newOp = new SyncOperation(syncOperation, pollFrequencyMillis, 3301 flexMillis); 3302 newOp.jobId = syncOperation.jobId; 3303 scheduleSyncOperationH(newOp); 3304 } 3305 } 3306 updateOrAddPeriodicSyncH(EndPoint target, long pollFrequency, long flex, Bundle extras)3307 private void updateOrAddPeriodicSyncH(EndPoint target, long pollFrequency, long flex, 3308 Bundle extras) { 3309 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 3310 verifyJobScheduler(); // Will fill in mScheduledSyncs cache if it is not already filled. 3311 final long pollFrequencyMillis = pollFrequency * 1000L; 3312 final long flexMillis = flex * 1000L; 3313 if (isLoggable) { 3314 Slog.v(TAG, "Addition to periodic syncs requested: " + target 3315 + " period: " + pollFrequency 3316 + " flexMillis: " + flex 3317 + " extras: " + extras.toString()); 3318 } 3319 List<SyncOperation> ops = getAllPendingSyncs(); 3320 for (SyncOperation op: ops) { 3321 if (op.isPeriodic && op.target.matchesSpec(target) 3322 && syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) { 3323 maybeUpdateSyncPeriodH(op, pollFrequencyMillis, flexMillis); 3324 return; 3325 } 3326 } 3327 3328 if (isLoggable) { 3329 Slog.v(TAG, "Adding new periodic sync: " + target 3330 + " period: " + pollFrequency 3331 + " flexMillis: " + flex 3332 + " extras: " + extras.toString()); 3333 } 3334 3335 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> 3336 syncAdapterInfo = mSyncAdapters.getServiceInfo( 3337 SyncAdapterType.newKey( 3338 target.provider, target.account.type), 3339 target.userId); 3340 if (syncAdapterInfo == null) { 3341 return; 3342 } 3343 3344 SyncOperation op = new SyncOperation(target, syncAdapterInfo.uid, 3345 syncAdapterInfo.componentName.getPackageName(), SyncOperation.REASON_PERIODIC, 3346 SyncStorageEngine.SOURCE_PERIODIC, extras, 3347 syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID, 3348 pollFrequencyMillis, flexMillis, ContentResolver.SYNC_EXEMPTION_NONE); 3349 3350 final int syncOpState = computeSyncOpState(op); 3351 switch (syncOpState) { 3352 case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS: { 3353 String packageName = op.owningPackage; 3354 final int userId = UserHandle.getUserId(op.owningUid); 3355 // If the app did not run and has no account access, done 3356 if (!wasPackageEverLaunched(packageName, userId)) { 3357 return; 3358 } 3359 mAccountManagerInternal.requestAccountAccess(op.target.account, 3360 packageName, userId, new RemoteCallback((Bundle result) -> { 3361 if (result != null 3362 && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) { 3363 updateOrAddPeriodicSync(target, pollFrequency, flex, extras); 3364 } 3365 } 3366 )); 3367 } return; 3368 3369 case SYNC_OP_STATE_INVALID: { 3370 return; 3371 } 3372 } 3373 3374 scheduleSyncOperationH(op); 3375 mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, 3376 target.userId); 3377 } 3378 3379 /** 3380 * Remove this periodic sync operation and all one-off operations initiated by it. 3381 */ removePeriodicSyncInternalH(SyncOperation syncOperation, String why)3382 private void removePeriodicSyncInternalH(SyncOperation syncOperation, String why) { 3383 // Remove this periodic sync and all one-off syncs initiated by it. 3384 List<SyncOperation> ops = getAllPendingSyncs(); 3385 for (SyncOperation op: ops) { 3386 if (op.sourcePeriodicId == syncOperation.jobId || op.jobId == syncOperation.jobId) { 3387 ActiveSyncContext asc = findActiveSyncContextH(syncOperation.jobId); 3388 if (asc != null) { 3389 SyncJobService.callJobFinished(syncOperation.jobId, false, 3390 "removePeriodicSyncInternalH"); 3391 runSyncFinishedOrCanceledH(null, asc); 3392 } 3393 mLogger.log("removePeriodicSyncInternalH-canceling: ", op); 3394 cancelJob(op, why); 3395 } 3396 } 3397 } 3398 removePeriodicSyncH(EndPoint target, Bundle extras, String why)3399 private void removePeriodicSyncH(EndPoint target, Bundle extras, String why) { 3400 verifyJobScheduler(); 3401 List<SyncOperation> ops = getAllPendingSyncs(); 3402 for (SyncOperation op: ops) { 3403 if (op.isPeriodic && op.target.matchesSpec(target) 3404 && syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) { 3405 removePeriodicSyncInternalH(op, why); 3406 } 3407 } 3408 } 3409 isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext)3410 private boolean isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext) { 3411 final long bytesTransferredCurrent = 3412 getTotalBytesTransferredByUid(activeSyncContext.mSyncAdapterUid); 3413 final long deltaBytesTransferred = 3414 bytesTransferredCurrent - activeSyncContext.mBytesTransferredAtLastPoll; 3415 3416 if (Log.isLoggable(TAG, Log.DEBUG)) { 3417 // Bytes transferred 3418 long remainder = deltaBytesTransferred; 3419 long mb = remainder / (1024 * 1024); 3420 remainder %= 1024 * 1024; 3421 long kb = remainder / 1024; 3422 remainder %= 1024; 3423 long b = remainder; 3424 Log.d(TAG, String.format( 3425 "Time since last update: %ds. Delta transferred: %dMBs,%dKBs,%dBs", 3426 (SystemClock.elapsedRealtime() 3427 - activeSyncContext.mLastPolledTimeElapsed)/1000, 3428 mb, kb, b) 3429 ); 3430 } 3431 return (deltaBytesTransferred <= SYNC_MONITOR_PROGRESS_THRESHOLD_BYTES); 3432 } 3433 3434 /** 3435 * Determine if a sync is no longer valid and should be dropped. 3436 */ computeSyncOpState(SyncOperation op)3437 private int computeSyncOpState(SyncOperation op) { 3438 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 3439 int state; 3440 final EndPoint target = op.target; 3441 3442 // Drop the sync if the account of this operation no longer exists. 3443 AccountAndUser[] accounts = mRunningAccounts; 3444 if (!containsAccountAndUser(accounts, target.account, target.userId)) { 3445 if (isLoggable) { 3446 Slog.v(TAG, " Dropping sync operation: account doesn't exist."); 3447 } 3448 return SYNC_OP_STATE_INVALID; 3449 } 3450 // Drop this sync request if it isn't syncable. 3451 state = computeSyncable(target.account, target.userId, target.provider, true); 3452 if (state == AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS) { 3453 if (isLoggable) { 3454 Slog.v(TAG, " Dropping sync operation: " 3455 + "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS"); 3456 } 3457 return SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS; 3458 } 3459 if (state == AuthorityInfo.NOT_SYNCABLE) { 3460 if (isLoggable) { 3461 Slog.v(TAG, " Dropping sync operation: isSyncable == NOT_SYNCABLE"); 3462 } 3463 return SYNC_OP_STATE_INVALID; 3464 } 3465 3466 final boolean syncEnabled = mSyncStorageEngine.getMasterSyncAutomatically(target.userId) 3467 && mSyncStorageEngine.getSyncAutomatically(target.account, 3468 target.userId, target.provider); 3469 3470 // We ignore system settings that specify the sync is invalid if: 3471 // 1) It's manual - we try it anyway. When/if it fails it will be rescheduled. 3472 // or 3473 // 2) it's an initialisation sync - we just need to connect to it. 3474 final boolean ignoreSystemConfiguration = op.isIgnoreSettings() || (state < 0); 3475 3476 // Sync not enabled. 3477 if (!syncEnabled && !ignoreSystemConfiguration) { 3478 if (isLoggable) { 3479 Slog.v(TAG, " Dropping sync operation: disallowed by settings/network."); 3480 } 3481 return SYNC_OP_STATE_INVALID; 3482 } 3483 return SYNC_OP_STATE_VALID; 3484 } 3485 dispatchSyncOperation(SyncOperation op)3486 private boolean dispatchSyncOperation(SyncOperation op) { 3487 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3488 Slog.v(TAG, "dispatchSyncOperation: we are going to sync " + op); 3489 Slog.v(TAG, "num active syncs: " + mActiveSyncContexts.size()); 3490 for (ActiveSyncContext syncContext : mActiveSyncContexts) { 3491 Slog.v(TAG, syncContext.toString()); 3492 } 3493 } 3494 if (op.isAppStandbyExempted()) { 3495 final UsageStatsManagerInternal usmi = LocalServices.getService( 3496 UsageStatsManagerInternal.class); 3497 if (usmi != null) { 3498 usmi.reportExemptedSyncStart(op.owningPackage, 3499 UserHandle.getUserId(op.owningUid)); 3500 } 3501 } 3502 3503 // Connect to the sync adapter. 3504 int targetUid; 3505 ComponentName targetComponent; 3506 final SyncStorageEngine.EndPoint info = op.target; 3507 SyncAdapterType syncAdapterType = 3508 SyncAdapterType.newKey(info.provider, info.account.type); 3509 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; 3510 syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, info.userId); 3511 if (syncAdapterInfo == null) { 3512 mLogger.log("dispatchSyncOperation() failed: no sync adapter info for ", 3513 syncAdapterType); 3514 Log.d(TAG, "can't find a sync adapter for " + syncAdapterType 3515 + ", removing settings for it"); 3516 mSyncStorageEngine.removeAuthority(info); 3517 return false; 3518 } 3519 targetUid = syncAdapterInfo.uid; 3520 targetComponent = syncAdapterInfo.componentName; 3521 ActiveSyncContext activeSyncContext = 3522 new ActiveSyncContext(op, insertStartSyncEvent(op), targetUid); 3523 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3524 Slog.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext); 3525 } 3526 3527 activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext); 3528 mActiveSyncContexts.add(activeSyncContext); 3529 3530 // Post message to begin monitoring this sync's progress. 3531 postMonitorSyncProgressMessage(activeSyncContext); 3532 3533 if (!activeSyncContext.bindToSyncAdapter(targetComponent, info.userId)) { 3534 mLogger.log("dispatchSyncOperation() failed: bind failed. target: ", 3535 targetComponent); 3536 Slog.e(TAG, "Bind attempt failed - target: " + targetComponent); 3537 closeActiveSyncContext(activeSyncContext); 3538 return false; 3539 } 3540 3541 return true; 3542 } 3543 runBoundToAdapterH(final ActiveSyncContext activeSyncContext, IBinder syncAdapter)3544 private void runBoundToAdapterH(final ActiveSyncContext activeSyncContext, 3545 IBinder syncAdapter) { 3546 final SyncOperation syncOperation = activeSyncContext.mSyncOperation; 3547 try { 3548 activeSyncContext.mIsLinkedToDeath = true; 3549 syncAdapter.linkToDeath(activeSyncContext, 0); 3550 3551 mLogger.log("Sync start: account=" + syncOperation.target.account, 3552 " authority=", syncOperation.target.provider, 3553 " reason=", SyncOperation.reasonToString(null, syncOperation.reason), 3554 " extras=", SyncOperation.extrasToString(syncOperation.extras), 3555 " adapter=", activeSyncContext.mSyncAdapter); 3556 3557 activeSyncContext.mSyncAdapter = ISyncAdapter.Stub.asInterface(syncAdapter); 3558 activeSyncContext.mSyncAdapter 3559 .startSync(activeSyncContext, syncOperation.target.provider, 3560 syncOperation.target.account, syncOperation.extras); 3561 3562 mLogger.log("Sync is running now..."); 3563 } catch (RemoteException remoteExc) { 3564 mLogger.log("Sync failed with RemoteException: ", remoteExc.toString()); 3565 Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc); 3566 closeActiveSyncContext(activeSyncContext); 3567 increaseBackoffSetting(syncOperation.target); 3568 scheduleSyncOperationH(syncOperation); 3569 } catch (RuntimeException exc) { 3570 mLogger.log("Sync failed with RuntimeException: ", exc.toString()); 3571 closeActiveSyncContext(activeSyncContext); 3572 Slog.e(TAG, "Caught RuntimeException while starting the sync " 3573 + logSafe(syncOperation), exc); 3574 } 3575 } 3576 3577 /** 3578 * Cancel the sync for the provided target that matches the given bundle. 3579 * @param info Can have null fields to indicate all the active syncs for that field. 3580 * @param extras Can be null to indicate <strong>all</strong> syncs for the given endpoint. 3581 */ cancelActiveSyncH(SyncStorageEngine.EndPoint info, Bundle extras, String why)3582 private void cancelActiveSyncH(SyncStorageEngine.EndPoint info, Bundle extras, 3583 String why) { 3584 ArrayList<ActiveSyncContext> activeSyncs = 3585 new ArrayList<ActiveSyncContext>(mActiveSyncContexts); 3586 for (ActiveSyncContext activeSyncContext : activeSyncs) { 3587 if (activeSyncContext != null) { 3588 final SyncStorageEngine.EndPoint opInfo = 3589 activeSyncContext.mSyncOperation.target; 3590 if (!opInfo.matchesSpec(info)) { 3591 continue; 3592 } 3593 if (extras != null && 3594 !syncExtrasEquals(activeSyncContext.mSyncOperation.extras, 3595 extras, 3596 false /* no config settings */)) { 3597 continue; 3598 } 3599 SyncJobService.callJobFinished(activeSyncContext.mSyncOperation.jobId, false, 3600 why); 3601 runSyncFinishedOrCanceledH(null /* cancel => no result */, activeSyncContext); 3602 } 3603 } 3604 } 3605 3606 /** 3607 * Should be called when a one-off instance of a periodic sync completes successfully. 3608 */ reschedulePeriodicSyncH(SyncOperation syncOperation)3609 private void reschedulePeriodicSyncH(SyncOperation syncOperation) { 3610 // Ensure that the periodic sync wasn't removed. 3611 SyncOperation periodicSync = null; 3612 List<SyncOperation> ops = getAllPendingSyncs(); 3613 for (SyncOperation op: ops) { 3614 if (op.isPeriodic && syncOperation.matchesPeriodicOperation(op)) { 3615 periodicSync = op; 3616 break; 3617 } 3618 } 3619 if (periodicSync == null) { 3620 return; 3621 } 3622 scheduleSyncOperationH(periodicSync); 3623 } 3624 runSyncFinishedOrCanceledH(SyncResult syncResult, ActiveSyncContext activeSyncContext)3625 private void runSyncFinishedOrCanceledH(SyncResult syncResult, 3626 ActiveSyncContext activeSyncContext) { 3627 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 3628 3629 final SyncOperation syncOperation = activeSyncContext.mSyncOperation; 3630 final SyncStorageEngine.EndPoint info = syncOperation.target; 3631 3632 if (activeSyncContext.mIsLinkedToDeath) { 3633 activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0); 3634 activeSyncContext.mIsLinkedToDeath = false; 3635 } 3636 final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime; 3637 String historyMessage; 3638 int downstreamActivity; 3639 int upstreamActivity; 3640 3641 mLogger.log("runSyncFinishedOrCanceledH() op=", syncOperation, " result=", syncResult); 3642 3643 if (syncResult != null) { 3644 if (isLoggable) { 3645 Slog.v(TAG, "runSyncFinishedOrCanceled [finished]: " 3646 + syncOperation + ", result " + syncResult); 3647 } 3648 3649 // In the non-canceled case, close the active sync context before doing the rest 3650 // of the stuff. 3651 closeActiveSyncContext(activeSyncContext); 3652 3653 // Note this part is probably okay to do before closeActiveSyncContext()... 3654 // But moved here to restore OC-dev's behavior. See b/64597061. 3655 if (!syncOperation.isPeriodic) { 3656 cancelJob(syncOperation, "runSyncFinishedOrCanceledH()-finished"); 3657 } 3658 3659 if (!syncResult.hasError()) { 3660 historyMessage = SyncStorageEngine.MESG_SUCCESS; 3661 // TODO: set these correctly when the SyncResult is extended to include it 3662 downstreamActivity = 0; 3663 upstreamActivity = 0; 3664 clearBackoffSetting(syncOperation.target, "sync success"); 3665 3666 // If the operation completes successfully and it was scheduled due to 3667 // a periodic operation failing, we reschedule the periodic operation to 3668 // start from now. 3669 if (syncOperation.isDerivedFromFailedPeriodicSync()) { 3670 reschedulePeriodicSyncH(syncOperation); 3671 } 3672 } else { 3673 Log.w(TAG, "failed sync operation " 3674 + logSafe(syncOperation) + ", " + syncResult); 3675 3676 syncOperation.retries++; 3677 if (syncOperation.retries > mConstants.getMaxRetriesWithAppStandbyExemption()) { 3678 syncOperation.syncExemptionFlag = ContentResolver.SYNC_EXEMPTION_NONE; 3679 } 3680 3681 // the operation failed so increase the backoff time 3682 increaseBackoffSetting(syncOperation.target); 3683 if (!syncOperation.isPeriodic) { 3684 // reschedule the sync if so indicated by the syncResult 3685 maybeRescheduleSync(syncResult, syncOperation); 3686 } else { 3687 // create a normal sync instance that will respect adapter backoffs 3688 postScheduleSyncMessage(syncOperation.createOneTimeSyncOperation(), 3689 0 /* min delay */); 3690 } 3691 historyMessage = ContentResolver.syncErrorToString( 3692 syncResultToErrorNumber(syncResult)); 3693 // TODO: set these correctly when the SyncResult is extended to include it 3694 downstreamActivity = 0; 3695 upstreamActivity = 0; 3696 } 3697 setDelayUntilTime(syncOperation.target, syncResult.delayUntil); 3698 } else { 3699 if (isLoggable) { 3700 Slog.v(TAG, "runSyncFinishedOrCanceled [canceled]: " + syncOperation); 3701 } 3702 3703 if (!syncOperation.isPeriodic) { 3704 cancelJob(syncOperation, "runSyncFinishedOrCanceledH()-canceled"); 3705 } 3706 3707 if (activeSyncContext.mSyncAdapter != null) { 3708 try { 3709 mLogger.log("Calling cancelSync for runSyncFinishedOrCanceled ", 3710 activeSyncContext, " adapter=", activeSyncContext.mSyncAdapter); 3711 activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext); 3712 mLogger.log("Canceled"); 3713 } catch (RemoteException e) { 3714 mLogger.log("RemoteException ", Log.getStackTraceString(e)); 3715 // we don't need to retry this in this case 3716 } 3717 } 3718 historyMessage = SyncStorageEngine.MESG_CANCELED; 3719 downstreamActivity = 0; 3720 upstreamActivity = 0; 3721 3722 // In the cancel sync case, close it after calling cancelSync(). 3723 closeActiveSyncContext(activeSyncContext); 3724 } 3725 3726 stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage, 3727 upstreamActivity, downstreamActivity, elapsedTime); 3728 // Check for full-resync and schedule it after closing off the last sync. 3729 if (syncResult != null && syncResult.tooManyDeletions) { 3730 installHandleTooManyDeletesNotification(info.account, 3731 info.provider, syncResult.stats.numDeletes, 3732 info.userId); 3733 } else { 3734 mNotificationMgr.cancelAsUser( 3735 Integer.toString(info.account.hashCode() ^ info.provider.hashCode()), 3736 SystemMessage.NOTE_SYNC_ERROR, 3737 new UserHandle(info.userId)); 3738 } 3739 if (syncResult != null && syncResult.fullSyncRequested) { 3740 scheduleSyncOperationH( 3741 new SyncOperation(info.account, info.userId, 3742 syncOperation.owningUid, syncOperation.owningPackage, 3743 syncOperation.reason, 3744 syncOperation.syncSource, info.provider, new Bundle(), 3745 syncOperation.allowParallelSyncs, 3746 syncOperation.syncExemptionFlag)); 3747 } 3748 } 3749 closeActiveSyncContext(ActiveSyncContext activeSyncContext)3750 private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) { 3751 activeSyncContext.close(); 3752 mActiveSyncContexts.remove(activeSyncContext); 3753 mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo, 3754 activeSyncContext.mSyncOperation.target.userId); 3755 3756 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3757 Slog.v(TAG, "removing all MESSAGE_MONITOR_SYNC & MESSAGE_SYNC_EXPIRED for " 3758 + activeSyncContext.toString()); 3759 } 3760 mSyncHandler.removeMessages(SyncHandler.MESSAGE_MONITOR_SYNC, activeSyncContext); 3761 3762 mLogger.log("closeActiveSyncContext: ", activeSyncContext); 3763 } 3764 3765 /** 3766 * Convert the error-containing SyncResult into the Sync.History error number. Since 3767 * the SyncResult may indicate multiple errors at once, this method just returns the 3768 * most "serious" error. 3769 * @param syncResult the SyncResult from which to read 3770 * @return the most "serious" error set in the SyncResult 3771 * @throws IllegalStateException if the SyncResult does not indicate any errors. 3772 * If SyncResult.error() is true then it is safe to call this. 3773 */ syncResultToErrorNumber(SyncResult syncResult)3774 private int syncResultToErrorNumber(SyncResult syncResult) { 3775 if (syncResult.syncAlreadyInProgress) 3776 return ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS; 3777 if (syncResult.stats.numAuthExceptions > 0) 3778 return ContentResolver.SYNC_ERROR_AUTHENTICATION; 3779 if (syncResult.stats.numIoExceptions > 0) 3780 return ContentResolver.SYNC_ERROR_IO; 3781 if (syncResult.stats.numParseExceptions > 0) 3782 return ContentResolver.SYNC_ERROR_PARSE; 3783 if (syncResult.stats.numConflictDetectedExceptions > 0) 3784 return ContentResolver.SYNC_ERROR_CONFLICT; 3785 if (syncResult.tooManyDeletions) 3786 return ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS; 3787 if (syncResult.tooManyRetries) 3788 return ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES; 3789 if (syncResult.databaseError) 3790 return ContentResolver.SYNC_ERROR_INTERNAL; 3791 throw new IllegalStateException("we are not in an error state, " + syncResult); 3792 } 3793 installHandleTooManyDeletesNotification(Account account, String authority, long numDeletes, int userId)3794 private void installHandleTooManyDeletesNotification(Account account, String authority, 3795 long numDeletes, int userId) { 3796 if (mNotificationMgr == null) return; 3797 3798 final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider( 3799 authority, 0 /* flags */); 3800 if (providerInfo == null) { 3801 return; 3802 } 3803 CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager()); 3804 3805 Intent clickIntent = new Intent(mContext, SyncActivityTooManyDeletes.class); 3806 clickIntent.putExtra("account", account); 3807 clickIntent.putExtra("authority", authority); 3808 clickIntent.putExtra("provider", authorityName.toString()); 3809 clickIntent.putExtra("numDeletes", numDeletes); 3810 3811 if (!isActivityAvailable(clickIntent)) { 3812 Log.w(TAG, "No activity found to handle too many deletes."); 3813 return; 3814 } 3815 3816 UserHandle user = new UserHandle(userId); 3817 final PendingIntent pendingIntent = PendingIntent 3818 .getActivityAsUser(mContext, 0, clickIntent, 3819 PendingIntent.FLAG_CANCEL_CURRENT, null, user); 3820 3821 CharSequence tooManyDeletesDescFormat = mContext.getResources().getText( 3822 R.string.contentServiceTooManyDeletesNotificationDesc); 3823 3824 Context contextForUser = getContextForUser(user); 3825 Notification notification = 3826 new Notification.Builder(contextForUser, SystemNotificationChannels.ACCOUNT) 3827 .setSmallIcon(R.drawable.stat_notify_sync_error) 3828 .setTicker(mContext.getString(R.string.contentServiceSync)) 3829 .setWhen(System.currentTimeMillis()) 3830 .setColor(contextForUser.getColor( 3831 com.android.internal.R.color.system_notification_accent_color)) 3832 .setContentTitle(contextForUser.getString( 3833 R.string.contentServiceSyncNotificationTitle)) 3834 .setContentText( 3835 String.format(tooManyDeletesDescFormat.toString(), authorityName)) 3836 .setContentIntent(pendingIntent) 3837 .build(); 3838 notification.flags |= Notification.FLAG_ONGOING_EVENT; 3839 mNotificationMgr.notifyAsUser( 3840 Integer.toString(account.hashCode() ^ authority.hashCode()), 3841 SystemMessage.NOTE_SYNC_ERROR, 3842 notification, user); 3843 } 3844 3845 /** 3846 * Checks whether an activity exists on the system image for the given intent. 3847 * 3848 * @param intent The intent for an activity. 3849 * @return Whether or not an activity exists. 3850 */ isActivityAvailable(Intent intent)3851 private boolean isActivityAvailable(Intent intent) { 3852 PackageManager pm = mContext.getPackageManager(); 3853 List<ResolveInfo> list = pm.queryIntentActivities(intent, 0); 3854 int listSize = list.size(); 3855 for (int i = 0; i < listSize; i++) { 3856 ResolveInfo resolveInfo = list.get(i); 3857 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 3858 != 0) { 3859 return true; 3860 } 3861 } 3862 3863 return false; 3864 } 3865 insertStartSyncEvent(SyncOperation syncOperation)3866 public long insertStartSyncEvent(SyncOperation syncOperation) { 3867 final long now = System.currentTimeMillis(); 3868 EventLog.writeEvent(2720, 3869 syncOperation.toEventLog(SyncStorageEngine.EVENT_START)); 3870 return mSyncStorageEngine.insertStartSyncEvent(syncOperation, now); 3871 } 3872 stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, int upstreamActivity, int downstreamActivity, long elapsedTime)3873 public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, 3874 int upstreamActivity, int downstreamActivity, long elapsedTime) { 3875 EventLog.writeEvent(2720, 3876 syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP)); 3877 mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime, 3878 resultMessage, downstreamActivity, upstreamActivity, 3879 syncOperation.target.userId); 3880 } 3881 } 3882 isSyncStillActiveH(ActiveSyncContext activeSyncContext)3883 private boolean isSyncStillActiveH(ActiveSyncContext activeSyncContext) { 3884 for (ActiveSyncContext sync : mActiveSyncContexts) { 3885 if (sync == activeSyncContext) { 3886 return true; 3887 } 3888 } 3889 return false; 3890 } 3891 3892 /** 3893 * Sync extra comparison function. 3894 * @param b1 bundle to compare 3895 * @param b2 other bundle to compare 3896 * @param includeSyncSettings if false, ignore system settings in bundle. 3897 */ syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSyncSettings)3898 public static boolean syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSyncSettings) { 3899 if (b1 == b2) { 3900 return true; 3901 } 3902 // Exit early if we can. 3903 if (includeSyncSettings && b1.size() != b2.size()) { 3904 return false; 3905 } 3906 Bundle bigger = b1.size() > b2.size() ? b1 : b2; 3907 Bundle smaller = b1.size() > b2.size() ? b2 : b1; 3908 for (String key : bigger.keySet()) { 3909 if (!includeSyncSettings && isSyncSetting(key)) { 3910 continue; 3911 } 3912 if (!smaller.containsKey(key)) { 3913 return false; 3914 } 3915 if (!Objects.equals(bigger.get(key), smaller.get(key))) { 3916 return false; 3917 } 3918 } 3919 return true; 3920 } 3921 3922 /** 3923 * @return true if the provided key is used by the SyncManager in scheduling the sync. 3924 */ isSyncSetting(String key)3925 private static boolean isSyncSetting(String key) { 3926 if (key.equals(ContentResolver.SYNC_EXTRAS_EXPEDITED)) { 3927 return true; 3928 } 3929 if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS)) { 3930 return true; 3931 } 3932 if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF)) { 3933 return true; 3934 } 3935 if (key.equals(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY)) { 3936 return true; 3937 } 3938 if (key.equals(ContentResolver.SYNC_EXTRAS_MANUAL)) { 3939 return true; 3940 } 3941 if (key.equals(ContentResolver.SYNC_EXTRAS_UPLOAD)) { 3942 return true; 3943 } 3944 if (key.equals(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS)) { 3945 return true; 3946 } 3947 if (key.equals(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS)) { 3948 return true; 3949 } 3950 if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD)) { 3951 return true; 3952 } 3953 if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD)) { 3954 return true; 3955 } 3956 if (key.equals(ContentResolver.SYNC_EXTRAS_PRIORITY)) { 3957 return true; 3958 } 3959 if (key.equals(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED)) { 3960 return true; 3961 } 3962 if (key.equals(ContentResolver.SYNC_EXTRAS_INITIALIZE)) { 3963 return true; 3964 } 3965 // if (key.equals(ContentResolver.SYNC_EXTRAS_APP_STANDBY_EXEMPTED)) { 3966 // return true; 3967 // } 3968 // No need to check virtual flags such as SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC. 3969 return false; 3970 } 3971 3972 static class PrintTable { 3973 private ArrayList<String[]> mTable = Lists.newArrayList(); 3974 private final int mCols; 3975 PrintTable(int cols)3976 PrintTable(int cols) { 3977 mCols = cols; 3978 } 3979 set(int row, int col, Object... values)3980 void set(int row, int col, Object... values) { 3981 if (col + values.length > mCols) { 3982 throw new IndexOutOfBoundsException("Table only has " + mCols + 3983 " columns. can't set " + values.length + " at column " + col); 3984 } 3985 for (int i = mTable.size(); i <= row; i++) { 3986 final String[] list = new String[mCols]; 3987 mTable.add(list); 3988 for (int j = 0; j < mCols; j++) { 3989 list[j] = ""; 3990 } 3991 } 3992 final String[] rowArray = mTable.get(row); 3993 for (int i = 0; i < values.length; i++) { 3994 final Object value = values[i]; 3995 rowArray[col + i] = (value == null) ? "" : value.toString(); 3996 } 3997 } 3998 writeTo(PrintWriter out)3999 void writeTo(PrintWriter out) { 4000 final String[] formats = new String[mCols]; 4001 int totalLength = 0; 4002 for (int col = 0; col < mCols; ++col) { 4003 int maxLength = 0; 4004 for (Object[] row : mTable) { 4005 final int length = row[col].toString().length(); 4006 if (length > maxLength) { 4007 maxLength = length; 4008 } 4009 } 4010 totalLength += maxLength; 4011 formats[col] = String.format("%%-%ds", maxLength); 4012 } 4013 formats[mCols - 1] = "%s"; 4014 printRow(out, formats, mTable.get(0)); 4015 totalLength += (mCols - 1) * 2; 4016 for (int i = 0; i < totalLength; ++i) { 4017 out.print("-"); 4018 } 4019 out.println(); 4020 for (int i = 1, mTableSize = mTable.size(); i < mTableSize; i++) { 4021 Object[] row = mTable.get(i); 4022 printRow(out, formats, row); 4023 } 4024 } 4025 printRow(PrintWriter out, String[] formats, Object[] row)4026 private void printRow(PrintWriter out, String[] formats, Object[] row) { 4027 for (int j = 0, rowLength = row.length; j < rowLength; j++) { 4028 out.printf(String.format(formats[j], row[j].toString())); 4029 out.print(" "); 4030 } 4031 out.println(); 4032 } 4033 getNumRows()4034 public int getNumRows() { 4035 return mTable.size(); 4036 } 4037 } 4038 getContextForUser(UserHandle user)4039 private Context getContextForUser(UserHandle user) { 4040 try { 4041 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user); 4042 } catch (NameNotFoundException e) { 4043 // Default to mContext, not finding the package system is running as is unlikely. 4044 return mContext; 4045 } 4046 } 4047 cancelJob(SyncOperation op, String why)4048 private void cancelJob(SyncOperation op, String why) { 4049 if (op == null) { 4050 Slog.wtf(TAG, "Null sync operation detected."); 4051 return; 4052 } 4053 if (op.isPeriodic) { 4054 mLogger.log("Removing periodic sync ", op, " for ", why); 4055 } 4056 getJobScheduler().cancel(op.jobId); 4057 } 4058 resetTodayStats()4059 public void resetTodayStats() { 4060 mSyncStorageEngine.resetTodayStats(/*force=*/ true); 4061 } 4062 wasPackageEverLaunched(String packageName, int userId)4063 private boolean wasPackageEverLaunched(String packageName, int userId) { 4064 try { 4065 return mPackageManagerInternal.wasPackageEverLaunched(packageName, userId); 4066 } catch (IllegalArgumentException e) { 4067 return false; // Package has been removed. 4068 } 4069 } 4070 } 4071