SyncManager.java revision 8c834c07675052c984728cbf79f7c2e0d8246e43
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 17package com.android.server.content; 18 19import android.accounts.Account; 20import android.accounts.AccountAndUser; 21import android.accounts.AccountManager; 22import android.app.ActivityManager; 23import android.app.ActivityManagerNative; 24import android.app.AppGlobals; 25import android.app.Notification; 26import android.app.NotificationManager; 27import android.app.PendingIntent; 28import android.app.job.JobInfo; 29import android.app.job.JobScheduler; 30import android.content.BroadcastReceiver; 31import android.content.ComponentName; 32import android.content.ContentResolver; 33import android.content.Context; 34import android.content.ISyncAdapter; 35import android.content.ISyncContext; 36import android.content.Intent; 37import android.content.IntentFilter; 38import android.content.PeriodicSync; 39import android.content.ServiceConnection; 40import android.content.SyncActivityTooManyDeletes; 41import android.content.SyncAdapterType; 42import android.content.SyncAdaptersCache; 43import android.content.SyncInfo; 44import android.content.SyncResult; 45import android.content.SyncStatusInfo; 46import android.content.pm.ApplicationInfo; 47import android.content.pm.PackageInfo; 48import android.content.pm.PackageManager; 49import android.content.pm.PackageManager.NameNotFoundException; 50import android.content.pm.ProviderInfo; 51import android.content.pm.RegisteredServicesCache; 52import android.content.pm.RegisteredServicesCacheListener; 53import android.content.pm.ResolveInfo; 54import android.content.pm.UserInfo; 55import android.database.ContentObserver; 56import android.net.ConnectivityManager; 57import android.net.NetworkInfo; 58import android.net.TrafficStats; 59import android.os.BatteryStats; 60import android.os.Bundle; 61import android.os.Handler; 62import android.os.IBinder; 63import android.os.Looper; 64import android.os.Message; 65import android.os.PowerManager; 66import android.os.RemoteException; 67import android.os.ServiceManager; 68import android.os.SystemClock; 69import android.os.SystemProperties; 70import android.os.UserHandle; 71import android.os.UserManager; 72import android.os.WorkSource; 73import android.os.Messenger; 74import android.provider.Settings; 75import android.text.format.DateUtils; 76import android.text.format.Time; 77import android.util.EventLog; 78import android.util.Log; 79import android.util.Slog; 80import android.util.Pair; 81 82import android.util.SparseArray; 83import com.android.internal.R; 84import com.android.internal.app.IBatteryStats; 85import com.android.internal.os.BackgroundThread; 86import com.android.internal.util.IndentingPrintWriter; 87import com.android.server.accounts.AccountManagerService; 88import com.android.server.content.SyncStorageEngine.AuthorityInfo; 89import com.android.server.content.SyncStorageEngine.EndPoint; 90import com.android.server.content.SyncStorageEngine.OnSyncRequestListener; 91import com.google.android.collect.Lists; 92import com.google.android.collect.Maps; 93 94import java.io.FileDescriptor; 95import java.io.PrintWriter; 96import java.util.ArrayList; 97import java.util.Random; 98import java.util.List; 99import java.util.HashSet; 100import java.util.Collection; 101import java.util.Collections; 102import java.util.Comparator; 103import java.util.Map; 104import java.util.Arrays; 105import java.util.HashMap; 106import java.util.Objects; 107 108/** 109 * Implementation details: 110 * All scheduled syncs will be passed on to JobScheduler as jobs 111 * (See {@link #scheduleSyncOperationH(SyncOperation, long)}. This function schedules a job 112 * with JobScheduler with appropriate delay and constraints (according to backoffs and extras). 113 * A local copy of each scheduled SyncOperation object is stored in {@link mScheduledSyncs}.This 114 * acts as a cache, so that we don't have to query JobScheduler every time we want to get a list of 115 * all scheduled operations. The scheduleSyncOperationH function also assigns a unique jobId to each 116 * SyncOperation. 117 * 118 * Periodic Syncs: 119 * Each periodic sync is scheduled as a periodic job. If a periodic sync fails, we create a new 120 * one off SyncOperation and set its {@link SyncOperation#sourcePeriodicId} field to the jobId of the 121 * periodic sync. We don't allow the periodic job to run while any job initiated by it is pending. 122 * 123 * Backoffs: 124 * Each {@link EndPoint} has a backoff associated with it. When a SyncOperation fails, we increase 125 * the backoff on the authority. Then we reschedule all syncs associated with that authority to 126 * run at a later time. Similarly, when a sync succeeds, backoff is cleared and all associated syncs 127 * are rescheduled. A rescheduled sync will get a new jobId. 128 * 129 * State of {@link mScheduledSyncs}: 130 * Every one-off SyncOperation will be put into this SparseArray when it is scheduled with 131 * JobScheduler. And it will be removed once JobScheduler has started the job. Periodic syncs work 132 * differently. They will always be present in mScheduledSyncs until the periodic sync is removed. 133 * This is to ensure that if a request to add a periodic sync comes in, we add a new one only if a 134 * duplicate doesn't exist. At every point of time, mScheduledSyncs and JobScheduler will show the 135 * same pending syncs. 136 * 137 * @hide 138 */ 139public class SyncManager { 140 static final String TAG = "SyncManager"; 141 142 /** Delay a sync due to local changes this long. In milliseconds */ 143 private static final long LOCAL_SYNC_DELAY; 144 145 static { 146 LOCAL_SYNC_DELAY = 147 SystemProperties.getLong("sync.local_sync_delay", 30 * 1000 /* 30 seconds */); 148 } 149 150 /** 151 * When retrying a sync for the first time use this delay. After that 152 * the retry time will double until it reached MAX_SYNC_RETRY_TIME. 153 * In milliseconds. 154 */ 155 private static final long INITIAL_SYNC_RETRY_TIME_IN_MS = 30 * 1000; // 30 seconds 156 157 /** 158 * Default the max sync retry time to this value. 159 */ 160 private static final long DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS = 60 * 60; // one hour 161 162 /** 163 * How long to wait before retrying a sync that failed due to one already being in progress. 164 */ 165 private static final int DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS = 10; 166 167 /** 168 * How often to periodically poll network traffic for an adapter performing a sync to determine 169 * whether progress is being made. 170 */ 171 private static final long SYNC_MONITOR_WINDOW_LENGTH_MILLIS = 60 * 1000; // 60 seconds 172 173 /** 174 * How many bytes must be transferred (Tx + Rx) over the period of time defined by 175 * {@link #SYNC_MONITOR_WINDOW_LENGTH_MILLIS} for the sync to be considered to be making 176 * progress. 177 */ 178 private static final int SYNC_MONITOR_PROGRESS_THRESHOLD_BYTES = 10; // 10 bytes 179 180 /** 181 * How long to delay each queued {@link SyncHandler} message that may have occurred before boot 182 * or befor the device became provisioned. 183 */ 184 private static final long PER_SYNC_BOOT_DELAY_MILLIS = 1000L; // 1 second 185 186 /** 187 * The maximum amount of time we're willing to delay syncs out of boot, after device has been 188 * provisioned, etc. 189 */ 190 private static final long MAX_SYNC_BOOT_DELAY_MILLIS = 60000L; // 1 minute 191 192 /** 193 * If a previously scheduled sync becomes ready and we are low on storage, it gets 194 * pushed back for this amount of time. 195 */ 196 private static final long SYNC_DELAY_ON_LOW_STORAGE = 60*60*1000; // 1 hour 197 198 /** 199 * If a sync becomes ready and it conflicts with an already running sync, it gets 200 * pushed back for this amount of time. 201 */ 202 private static final long SYNC_DELAY_ON_CONFLICT = 10*1000; // 10 seconds 203 204 private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*/"; 205 private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm"; 206 private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock"; 207 208 private Context mContext; 209 210 private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0]; 211 212 // TODO: add better locking around mRunningAccounts 213 private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY; 214 215 volatile private PowerManager.WakeLock mHandleAlarmWakeLock; 216 volatile private PowerManager.WakeLock mSyncManagerWakeLock; 217 volatile private boolean mDataConnectionIsConnected = false; 218 volatile private boolean mStorageIsLow = false; 219 volatile private boolean mDeviceIsIdle = false; 220 volatile private boolean mReportedSyncActive = false; 221 222 private final NotificationManager mNotificationMgr; 223 private final IBatteryStats mBatteryStats; 224 private JobScheduler mJobScheduler; 225 private SyncJobService mSyncJobService; 226 227 private SyncStorageEngine mSyncStorageEngine; 228 229 protected final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList(); 230 231 // Synchronized on "this". Instead of using this directly one should instead call 232 // its accessor, getConnManager(). 233 private ConnectivityManager mConnManagerDoNotUseDirectly; 234 235 /** Track whether the device has already been provisioned. */ 236 private volatile boolean mProvisioned; 237 238 protected SyncAdaptersCache mSyncAdapters; 239 240 // Cache of all operations scheduled on the JobScheduler so that JobScheduler doesn't have 241 // to be queried often. 242 private SparseArray<SyncOperation> mScheduledSyncs = new SparseArray<SyncOperation>(32); 243 private final Random mRand; 244 245 private int getUnusedJobId() { 246 synchronized (mScheduledSyncs) { 247 int newJobId = mRand.nextInt(Integer.MAX_VALUE); 248 while (mScheduledSyncs.indexOfKey(newJobId) >= 0) { 249 newJobId = mRand.nextInt(Integer.MAX_VALUE); 250 } 251 return newJobId; 252 } 253 } 254 255 private void addSyncOperationToCache(SyncOperation op) { 256 synchronized (mScheduledSyncs) { 257 mScheduledSyncs.put(op.jobId, op); 258 } 259 } 260 261 private void removeSyncOperationFromCache(int jobId) { 262 synchronized (mScheduledSyncs) { 263 mScheduledSyncs.remove(jobId); 264 } 265 } 266 267 private List<SyncOperation> getAllPendingSyncsFromCache() { 268 synchronized (mScheduledSyncs) { 269 List<SyncOperation> pending = new ArrayList<SyncOperation>(mScheduledSyncs.size()); 270 for (int i=0; i<mScheduledSyncs.size(); i++) { 271 pending.add(mScheduledSyncs.valueAt(i)); 272 } 273 return pending; 274 } 275 } 276 277 private final BroadcastReceiver mStorageIntentReceiver = 278 new BroadcastReceiver() { 279 @Override 280 public void onReceive(Context context, Intent intent) { 281 String action = intent.getAction(); 282 if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) { 283 if (Log.isLoggable(TAG, Log.VERBOSE)) { 284 Slog.v(TAG, "Internal storage is low."); 285 } 286 mStorageIsLow = true; 287 cancelActiveSync( 288 SyncStorageEngine.EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL, 289 null /* any sync */); 290 } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { 291 if (Log.isLoggable(TAG, Log.VERBOSE)) { 292 Slog.v(TAG, "Internal storage is ok."); 293 } 294 mStorageIsLow = false; 295 rescheduleSyncs(EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL); 296 } 297 } 298 }; 299 300 private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { 301 @Override 302 public void onReceive(Context context, Intent intent) { 303 mBootCompleted = true; 304 // Called because it gets all pending jobs and stores them in mScheduledSyncs cache. 305 verifyJobScheduler(); 306 mSyncHandler.onBootCompleted(); 307 } 308 }; 309 310 private final BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() { 311 @Override 312 public void onReceive(Context context, Intent intent) { 313 updateRunningAccounts(EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL 314 /* sync all targets */); 315 } 316 }; 317 318 private final PowerManager mPowerManager; 319 320 private final UserManager mUserManager; 321 322 private List<UserInfo> getAllUsers() { 323 return mUserManager.getUsers(); 324 } 325 326 private boolean containsAccountAndUser(AccountAndUser[] accounts, Account account, int userId) { 327 boolean found = false; 328 for (int i = 0; i < accounts.length; i++) { 329 if (accounts[i].userId == userId 330 && accounts[i].account.equals(account)) { 331 found = true; 332 break; 333 } 334 } 335 return found; 336 } 337 338 /** target indicates endpoints that should be synced after account info is updated. */ 339 private void updateRunningAccounts(EndPoint target) { 340 if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_ACCOUNTS_UPDATED"); 341 // Update accounts in handler thread. 342 Message m = mSyncHandler.obtainMessage(SyncHandler.MESSAGE_ACCOUNTS_UPDATED); 343 m.obj = target; 344 m.sendToTarget(); 345 } 346 347 private void doDatabaseCleanup() { 348 for (UserInfo user : mUserManager.getUsers(true)) { 349 // Skip any partially created/removed users 350 if (user.partial) continue; 351 Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts( 352 user.id, mContext.getOpPackageName()); 353 354 mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id); 355 } 356 } 357 358 private BroadcastReceiver mConnectivityIntentReceiver = 359 new BroadcastReceiver() { 360 @Override 361 public void onReceive(Context context, Intent intent) { 362 final boolean wasConnected = mDataConnectionIsConnected; 363 364 // Don't use the intent to figure out if network is connected, just check 365 // ConnectivityManager directly. 366 mDataConnectionIsConnected = readDataConnectionState(); 367 if (mDataConnectionIsConnected) { 368 if (!wasConnected) { 369 if (Log.isLoggable(TAG, Log.VERBOSE)) { 370 Slog.v(TAG, "Reconnection detected: clearing all backoffs"); 371 } 372 } 373 clearAllBackoffs(); 374 } 375 } 376 }; 377 378 private void clearAllBackoffs() { 379 mSyncStorageEngine.clearAllBackoffsLocked(); 380 rescheduleSyncs(EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL); 381 } 382 383 private boolean readDataConnectionState() { 384 NetworkInfo networkInfo = getConnectivityManager().getActiveNetworkInfo(); 385 return (networkInfo != null) && networkInfo.isConnected(); 386 } 387 388 private BroadcastReceiver mShutdownIntentReceiver = 389 new BroadcastReceiver() { 390 @Override 391 public void onReceive(Context context, Intent intent) { 392 Log.w(TAG, "Writing sync state before shutdown..."); 393 getSyncStorageEngine().writeAllState(); 394 } 395 }; 396 397 private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() { 398 @Override 399 public void onReceive(Context context, Intent intent) { 400 String action = intent.getAction(); 401 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 402 if (userId == UserHandle.USER_NULL) return; 403 404 if (Intent.ACTION_USER_REMOVED.equals(action)) { 405 onUserRemoved(userId); 406 } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { 407 onUserUnlocked(userId); 408 } else if (Intent.ACTION_USER_STOPPING.equals(action)) { 409 onUserStopping(userId); 410 } 411 } 412 }; 413 414 private final SyncHandler mSyncHandler; 415 416 private volatile boolean mBootCompleted = false; 417 418 private ConnectivityManager getConnectivityManager() { 419 synchronized (this) { 420 if (mConnManagerDoNotUseDirectly == null) { 421 mConnManagerDoNotUseDirectly = (ConnectivityManager)mContext.getSystemService( 422 Context.CONNECTIVITY_SERVICE); 423 } 424 return mConnManagerDoNotUseDirectly; 425 } 426 } 427 428 private synchronized void verifyJobScheduler() { 429 if (mJobScheduler != null) { 430 return; 431 } 432 if (Log.isLoggable(TAG, Log.VERBOSE)) { 433 Log.d(TAG, "initializing JobScheduler object."); 434 } 435 mJobScheduler = (JobScheduler) mContext.getSystemService( 436 Context.JOB_SCHEDULER_SERVICE); 437 // Get all persisted syncs from JobScheduler 438 List<JobInfo> pendingJobs = mJobScheduler.getAllPendingJobs(); 439 synchronized (mScheduledSyncs) { 440 for (JobInfo job : pendingJobs) { 441 SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras()); 442 if (op != null) { 443 mScheduledSyncs.put(op.jobId, op); 444 if (!op.isPeriodic) { 445 // Set the pending status of this EndPoint to true. Pending icon is 446 // shown on the settings activity. 447 mSyncStorageEngine.markPending(op.target, true); 448 } 449 } 450 } 451 } 452 } 453 454 private JobScheduler getJobScheduler() { 455 verifyJobScheduler(); 456 return mJobScheduler; 457 } 458 459 /** 460 * Should only be created after {@link ContentService#systemReady()} so that 461 * {@link PackageManager} is ready to query. 462 */ 463 public SyncManager(Context context, boolean factoryTest) { 464 // Initialize the SyncStorageEngine first, before registering observers 465 // and creating threads and so on; it may fail if the disk is full. 466 mContext = context; 467 468 SyncStorageEngine.init(context); 469 mSyncStorageEngine = SyncStorageEngine.getSingleton(); 470 mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() { 471 @Override 472 public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras) { 473 scheduleSync(info.account, info.userId, reason, info.provider, extras, 474 0 /* no flexMillis */, 475 0 /* run immediately */, 476 false); 477 } 478 }); 479 480 mSyncStorageEngine.setPeriodicSyncAddedListener( 481 new SyncStorageEngine.PeriodicSyncAddedListener() { 482 @Override 483 public void onPeriodicSyncAdded(EndPoint target, Bundle extras, long pollFrequency, 484 long flex) { 485 updateOrAddPeriodicSync(target, pollFrequency, flex, extras); 486 } 487 }); 488 489 mSyncStorageEngine.setOnAuthorityRemovedListener(new SyncStorageEngine.OnAuthorityRemovedListener() { 490 @Override 491 public void onAuthorityRemoved(EndPoint removedAuthority) { 492 removeSyncsForAuthority(removedAuthority); 493 } 494 }); 495 496 mSyncAdapters = new SyncAdaptersCache(mContext); 497 498 mSyncHandler = new SyncHandler(BackgroundThread.get().getLooper()); 499 500 mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() { 501 @Override 502 public void onServiceChanged(SyncAdapterType type, int userId, boolean removed) { 503 if (!removed) { 504 scheduleSync(null, UserHandle.USER_ALL, 505 SyncOperation.REASON_SERVICE_CHANGED, 506 type.authority, null, 0 /* no delay */, 0 /* no delay */, 507 false /* onlyThoseWithUnkownSyncableState */); 508 } 509 } 510 }, mSyncHandler); 511 512 mRand = new Random(System.currentTimeMillis()); 513 514 IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); 515 context.registerReceiver(mConnectivityIntentReceiver, intentFilter); 516 517 if (!factoryTest) { 518 intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); 519 intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 520 context.registerReceiver(mBootCompletedReceiver, intentFilter); 521 } 522 523 intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW); 524 intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); 525 context.registerReceiver(mStorageIntentReceiver, intentFilter); 526 527 intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN); 528 intentFilter.setPriority(100); 529 context.registerReceiver(mShutdownIntentReceiver, intentFilter); 530 531 intentFilter = new IntentFilter(); 532 intentFilter.addAction(Intent.ACTION_USER_REMOVED); 533 intentFilter.addAction(Intent.ACTION_USER_UNLOCKED); 534 intentFilter.addAction(Intent.ACTION_USER_STOPPING); 535 mContext.registerReceiverAsUser( 536 mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null); 537 538 if (!factoryTest) { 539 mNotificationMgr = (NotificationManager) 540 context.getSystemService(Context.NOTIFICATION_SERVICE); 541 } else { 542 mNotificationMgr = null; 543 } 544 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 545 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 546 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( 547 BatteryStats.SERVICE_NAME)); 548 549 // This WakeLock is used to ensure that we stay awake between the time that we receive 550 // a sync alarm notification and when we finish processing it. We need to do this 551 // because we don't do the work in the alarm handler, rather we do it in a message 552 // handler. 553 mHandleAlarmWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 554 HANDLE_SYNC_ALARM_WAKE_LOCK); 555 mHandleAlarmWakeLock.setReferenceCounted(false); 556 557 // This WakeLock is used to ensure that we stay awake while running the sync loop 558 // message handler. Normally we will hold a sync adapter wake lock while it is being 559 // synced but during the execution of the sync loop it might finish a sync for 560 // one sync adapter before starting the sync for the other sync adapter and we 561 // don't want the device to go to sleep during that window. 562 mSyncManagerWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 563 SYNC_LOOP_WAKE_LOCK); 564 mSyncManagerWakeLock.setReferenceCounted(false); 565 566 mProvisioned = isDeviceProvisioned(); 567 if (!mProvisioned) { 568 final ContentResolver resolver = context.getContentResolver(); 569 ContentObserver provisionedObserver = 570 new ContentObserver(null /* current thread */) { 571 public void onChange(boolean selfChange) { 572 mProvisioned |= isDeviceProvisioned(); 573 if (mProvisioned) { 574 mSyncHandler.onDeviceProvisioned(); 575 resolver.unregisterContentObserver(this); 576 } 577 } 578 }; 579 580 synchronized (mSyncHandler) { 581 resolver.registerContentObserver( 582 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), 583 false /* notifyForDescendents */, 584 provisionedObserver); 585 586 // The device *may* have been provisioned while we were registering above observer. 587 // Check again to make sure. 588 mProvisioned |= isDeviceProvisioned(); 589 if (mProvisioned) { 590 resolver.unregisterContentObserver(provisionedObserver); 591 } 592 } 593 } 594 595 if (!factoryTest) { 596 // Register for account list updates for all users 597 mContext.registerReceiverAsUser(mAccountsUpdatedReceiver, 598 UserHandle.ALL, 599 new IntentFilter(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION), 600 null, null); 601 } 602 603 Intent startServiceIntent = new Intent(mContext, SyncJobService.class); 604 startServiceIntent.putExtra(SyncJobService.EXTRA_MESSENGER, new Messenger(mSyncHandler)); 605 mContext.startService(startServiceIntent); 606 } 607 608 private boolean isDeviceProvisioned() { 609 final ContentResolver resolver = mContext.getContentResolver(); 610 return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0); 611 } 612 /** 613 * Return a random value v that satisfies minValue <= v < maxValue. The difference between 614 * maxValue and minValue must be less than Integer.MAX_VALUE. 615 */ 616 private long jitterize(long minValue, long maxValue) { 617 Random random = new Random(SystemClock.elapsedRealtime()); 618 long spread = maxValue - minValue; 619 if (spread > Integer.MAX_VALUE) { 620 throw new IllegalArgumentException("the difference between the maxValue and the " 621 + "minValue must be less than " + Integer.MAX_VALUE); 622 } 623 return minValue + random.nextInt((int)spread); 624 } 625 626 public SyncStorageEngine getSyncStorageEngine() { 627 return mSyncStorageEngine; 628 } 629 630 public int getIsSyncable(Account account, int userId, String providerName) { 631 int isSyncable = mSyncStorageEngine.getIsSyncable(account, userId, providerName); 632 UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId); 633 634 // If it's not a restricted user, return isSyncable. 635 if (userInfo == null || !userInfo.isRestricted()) return isSyncable; 636 637 // Else check if the sync adapter has opted-in or not. 638 RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = 639 mSyncAdapters.getServiceInfo( 640 SyncAdapterType.newKey(providerName, account.type), userId); 641 if (syncAdapterInfo == null) return isSyncable; 642 643 PackageInfo pInfo = null; 644 try { 645 pInfo = AppGlobals.getPackageManager().getPackageInfo( 646 syncAdapterInfo.componentName.getPackageName(), 0, userId); 647 if (pInfo == null) return isSyncable; 648 } catch (RemoteException re) { 649 // Shouldn't happen. 650 return isSyncable; 651 } 652 if (pInfo.restrictedAccountType != null 653 && pInfo.restrictedAccountType.equals(account.type)) { 654 return isSyncable; 655 } else { 656 return 0; 657 } 658 } 659 660 private void setAuthorityPendingState(EndPoint info) { 661 List<SyncOperation> ops = getAllPendingSyncsFromCache(); 662 for (SyncOperation op: ops) { 663 if (!op.isPeriodic && op.target.matchesSpec(info)) { 664 getSyncStorageEngine().markPending(info, true); 665 return; 666 } 667 } 668 getSyncStorageEngine().markPending(info, false); 669 } 670 671 /** 672 * Initiate a sync. This can start a sync for all providers 673 * (pass null to url, set onlyTicklable to false), only those 674 * providers that are marked as ticklable (pass null to url, 675 * set onlyTicklable to true), or a specific provider (set url 676 * to the content url of the provider). 677 * 678 * <p>If the ContentResolver.SYNC_EXTRAS_UPLOAD boolean in extras is 679 * true then initiate a sync that just checks for local changes to send 680 * to the server, otherwise initiate a sync that first gets any 681 * changes from the server before sending local changes back to 682 * the server. 683 * 684 * <p>If a specific provider is being synced (the url is non-null) 685 * then the extras can contain SyncAdapter-specific information 686 * to control what gets synced (e.g. which specific feed to sync). 687 * 688 * <p>You'll start getting callbacks after this. 689 * 690 * @param requestedAccount the account to sync, may be null to signify all accounts 691 * @param userId the id of the user whose accounts are to be synced. If userId is USER_ALL, 692 * then all users' accounts are considered. 693 * @param reason for sync request. If this is a positive integer, it is the Linux uid 694 * assigned to the process that requested the sync. If it's negative, the sync was requested by 695 * the SyncManager itself and could be one of the following: 696 * {@link SyncOperation#REASON_BACKGROUND_DATA_SETTINGS_CHANGED} 697 * {@link SyncOperation#REASON_ACCOUNTS_UPDATED} 698 * {@link SyncOperation#REASON_SERVICE_CHANGED} 699 * {@link SyncOperation#REASON_PERIODIC} 700 * {@link SyncOperation#REASON_IS_SYNCABLE} 701 * {@link SyncOperation#REASON_SYNC_AUTO} 702 * {@link SyncOperation#REASON_MASTER_SYNC_AUTO} 703 * {@link SyncOperation#REASON_USER_START} 704 * @param requestedAuthority the authority to sync, may be null to indicate all authorities 705 * @param extras a Map of SyncAdapter-specific information to control 706 * syncs of a specific provider. Can be null. Is ignored 707 * if the url is null. 708 * @param beforeRuntimeMillis milliseconds before runtimeMillis that this sync can run. 709 * @param runtimeMillis maximum milliseconds in the future to wait before performing sync. 710 * @param onlyThoseWithUnkownSyncableState Only sync authorities that have unknown state. 711 */ 712 public void scheduleSync(Account requestedAccount, int userId, int reason, 713 String requestedAuthority, Bundle extras, long beforeRuntimeMillis, 714 long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) { 715 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 716 EndPoint ep = new EndPoint(requestedAccount,requestedAuthority, userId); 717 if (extras == null) { 718 extras = new Bundle(); 719 } 720 if (isLoggable) { 721 Log.d(TAG, "one-time sync for: " + requestedAccount + " " + extras.toString() + " " 722 + requestedAuthority); 723 } 724 725 AccountAndUser[] accounts; 726 if (requestedAccount != null && userId != UserHandle.USER_ALL) { 727 accounts = new AccountAndUser[] { new AccountAndUser(requestedAccount, userId) }; 728 } else { 729 accounts = mRunningAccounts; 730 if (accounts.length == 0) { 731 if (isLoggable) { 732 Slog.v(TAG, "scheduleSync: no accounts configured, dropping"); 733 } 734 return; 735 } 736 } 737 738 final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false); 739 final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false); 740 if (manualSync) { 741 extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); 742 extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); 743 } 744 final boolean ignoreSettings = 745 extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false); 746 747 int source; 748 if (uploadOnly) { 749 source = SyncStorageEngine.SOURCE_LOCAL; 750 } else if (manualSync) { 751 source = SyncStorageEngine.SOURCE_USER; 752 } else if (requestedAuthority == null) { 753 source = SyncStorageEngine.SOURCE_POLL; 754 } else { 755 // This isn't strictly server, since arbitrary callers can (and do) request 756 // a non-forced two-way sync on a specific url. 757 source = SyncStorageEngine.SOURCE_SERVER; 758 } 759 760 for (AccountAndUser account : accounts) { 761 // If userId is specified, do not sync accounts of other users 762 if (userId >= UserHandle.USER_SYSTEM && account.userId >= UserHandle.USER_SYSTEM 763 && userId != account.userId) { 764 continue; 765 } 766 // Compile a list of authorities that have sync adapters. 767 // For each authority sync each account that matches a sync adapter. 768 final HashSet<String> syncableAuthorities = new HashSet<String>(); 769 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter : 770 mSyncAdapters.getAllServices(account.userId)) { 771 syncableAuthorities.add(syncAdapter.type.authority); 772 } 773 774 // If the url was specified then replace the list of authorities 775 // with just this authority or clear it if this authority isn't 776 // syncable. 777 if (requestedAuthority != null) { 778 final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority); 779 syncableAuthorities.clear(); 780 if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority); 781 } 782 783 for (String authority : syncableAuthorities) { 784 int isSyncable = getIsSyncable(account.account, account.userId, 785 authority); 786 if (isSyncable == AuthorityInfo.NOT_SYNCABLE) { 787 continue; 788 } 789 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; 790 syncAdapterInfo = mSyncAdapters.getServiceInfo( 791 SyncAdapterType.newKey(authority, account.account.type), account.userId); 792 if (syncAdapterInfo == null) { 793 continue; 794 } 795 final int owningUid = syncAdapterInfo.uid; 796 final String owningPackage = syncAdapterInfo.componentName.getPackageName(); 797 try { 798 if (ActivityManagerNative.getDefault().getAppStartMode(owningUid, 799 owningPackage) == ActivityManager.APP_START_MODE_DISABLED) { 800 Slog.w(TAG, "Not scheduling job " + syncAdapterInfo.uid + ":" 801 + syncAdapterInfo.componentName 802 + " -- package not allowed to start"); 803 continue; 804 } 805 } catch (RemoteException e) { 806 } 807 final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs(); 808 final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable(); 809 if (isSyncable < 0 && isAlwaysSyncable) { 810 mSyncStorageEngine.setIsSyncable( 811 account.account, account.userId, authority, AuthorityInfo.SYNCABLE); 812 isSyncable = AuthorityInfo.SYNCABLE; 813 } 814 if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) { 815 continue; 816 } 817 if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) { 818 continue; 819 } 820 821 boolean syncAllowed = 822 (isSyncable < 0) // Always allow if the isSyncable state is unknown. 823 || ignoreSettings 824 || (mSyncStorageEngine.getMasterSyncAutomatically(account.userId) 825 && mSyncStorageEngine.getSyncAutomatically(account.account, 826 account.userId, authority)); 827 if (!syncAllowed) { 828 if (isLoggable) { 829 Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority 830 + " is not allowed, dropping request"); 831 } 832 continue; 833 } 834 SyncStorageEngine.EndPoint info = 835 new SyncStorageEngine.EndPoint( 836 account.account, authority, account.userId); 837 long delayUntil = 838 mSyncStorageEngine.getDelayUntilTime(info); 839 if (isSyncable < 0) { 840 // Initialisation sync. 841 Bundle newExtras = new Bundle(); 842 newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true); 843 if (isLoggable) { 844 Slog.v(TAG, "schedule initialisation Sync:" 845 + ", delay until " + delayUntil 846 + ", run by " + 0 847 + ", flexMillis " + 0 848 + ", source " + source 849 + ", account " + account 850 + ", authority " + authority 851 + ", extras " + newExtras); 852 } 853 postScheduleSyncMessage( 854 new SyncOperation(account.account, account.userId, 855 owningUid, owningPackage, reason, source, 856 authority, newExtras, allowParallelSyncs) 857 ); 858 } 859 if (!onlyThoseWithUnkownSyncableState) { 860 if (isLoggable) { 861 Slog.v(TAG, "scheduleSync:" 862 + " delay until " + delayUntil 863 + " run by " + runtimeMillis 864 + " flexMillis " + beforeRuntimeMillis 865 + ", source " + source 866 + ", account " + account 867 + ", authority " + authority 868 + ", extras " + extras); 869 } 870 postScheduleSyncMessage( 871 new SyncOperation(account.account, account.userId, 872 owningUid, owningPackage, reason, source, 873 authority, extras, allowParallelSyncs) 874 ); 875 } 876 } 877 } 878 } 879 880 private void removeSyncsForAuthority(EndPoint info) { 881 verifyJobScheduler(); 882 List<SyncOperation> ops = getAllPendingSyncsFromCache(); 883 for (SyncOperation op: ops) { 884 if (op.target.matchesSpec(info)) { 885 removeSyncOperationFromCache(op.jobId); 886 getJobScheduler().cancel(op.jobId); 887 } 888 } 889 } 890 891 /** 892 * Remove a specific periodic sync identified by its target and extras. 893 */ 894 public void removePeriodicSync(EndPoint target, Bundle extras) { 895 Message m = mSyncHandler.obtainMessage(mSyncHandler.MESSAGE_REMOVE_PERIODIC_SYNC, target); 896 m.setData(extras); 897 m.sendToTarget(); 898 } 899 900 /** 901 * Add a periodic sync. If a sync with same target and extras exists, its period and 902 * flexMillis will be updated. 903 */ 904 public void updateOrAddPeriodicSync(EndPoint target, long pollFrequency, long flex, 905 Bundle extras) { 906 UpdatePeriodicSyncMessagePayload payload = new UpdatePeriodicSyncMessagePayload(target, 907 pollFrequency, flex, extras); 908 mSyncHandler.obtainMessage(SyncHandler.MESSAGE_UPDATE_PERIODIC_SYNC, payload) 909 .sendToTarget(); 910 } 911 912 /** 913 * Get a list of periodic syncs corresponding to the given target. 914 */ 915 public List<PeriodicSync> getPeriodicSyncs(EndPoint target) { 916 List<SyncOperation> ops = getAllPendingSyncsFromCache(); 917 List<PeriodicSync> periodicSyncs = new ArrayList<PeriodicSync>(); 918 919 for (SyncOperation op: ops) { 920 if (op.isPeriodic && op.target.matchesSpec(target)) { 921 periodicSyncs.add(new PeriodicSync(op.target.account, op.target.provider, 922 op.extras, op.periodMillis / 1000, op.flexMillis / 1000)); 923 } 924 } 925 926 return periodicSyncs; 927 } 928 929 /** 930 * Schedule sync based on local changes to a provider. Occurs within interval 931 * [LOCAL_SYNC_DELAY, 2*LOCAL_SYNC_DELAY]. 932 */ 933 public void scheduleLocalSync(Account account, int userId, int reason, String authority) { 934 final Bundle extras = new Bundle(); 935 extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true); 936 scheduleSync(account, userId, reason, authority, extras, 937 LOCAL_SYNC_DELAY /* earliest run time */, 938 2 * LOCAL_SYNC_DELAY /* latest sync time. */, 939 false /* onlyThoseWithUnkownSyncableState */); 940 } 941 942 public SyncAdapterType[] getSyncAdapterTypes(int userId) { 943 final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos; 944 serviceInfos = mSyncAdapters.getAllServices(userId); 945 SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()]; 946 int i = 0; 947 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) { 948 types[i] = serviceInfo.type; 949 ++i; 950 } 951 return types; 952 } 953 954 public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) { 955 return mSyncAdapters.getSyncAdapterPackagesForAuthority(authority, userId); 956 } 957 958 private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext, 959 SyncResult syncResult) { 960 if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_SYNC_FINISHED"); 961 Message msg = mSyncHandler.obtainMessage(); 962 msg.what = SyncHandler.MESSAGE_SYNC_FINISHED; 963 msg.obj = new SyncFinishedOrCancelledMessagePayload(syncContext, syncResult); 964 mSyncHandler.sendMessage(msg); 965 } 966 967 private void sendCancelSyncsMessage(final SyncStorageEngine.EndPoint info, Bundle extras) { 968 if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_CANCEL"); 969 Message msg = mSyncHandler.obtainMessage(); 970 msg.what = SyncHandler.MESSAGE_CANCEL; 971 msg.setData(extras); 972 msg.obj = info; 973 mSyncHandler.sendMessage(msg); 974 } 975 976 /** 977 * Post a delayed message that will monitor the given sync context by periodically checking how 978 * much network has been used by the uid. 979 */ 980 private void postMonitorSyncProgressMessage(ActiveSyncContext activeSyncContext) { 981 if (Log.isLoggable(TAG, Log.VERBOSE)) { 982 Slog.v(TAG, "posting MESSAGE_SYNC_MONITOR in " + 983 (SYNC_MONITOR_WINDOW_LENGTH_MILLIS/1000) + "s"); 984 } 985 986 activeSyncContext.mBytesTransferredAtLastPoll = 987 getTotalBytesTransferredByUid(activeSyncContext.mSyncAdapterUid); 988 activeSyncContext.mLastPolledTimeElapsed = SystemClock.elapsedRealtime(); 989 Message monitorMessage = 990 mSyncHandler.obtainMessage( 991 SyncHandler.MESSAGE_MONITOR_SYNC, 992 activeSyncContext); 993 mSyncHandler.sendMessageDelayed(monitorMessage, SYNC_MONITOR_WINDOW_LENGTH_MILLIS); 994 } 995 996 private void postScheduleSyncMessage(SyncOperation syncOperation) { 997 mSyncHandler.obtainMessage(mSyncHandler.MESSAGE_SCHEDULE_SYNC, syncOperation) 998 .sendToTarget(); 999 } 1000 1001 /** 1002 * Monitor sync progress by calculating how many bytes it is managing to send to and fro. 1003 */ 1004 private long getTotalBytesTransferredByUid(int uid) { 1005 return (TrafficStats.getUidRxBytes(uid) + TrafficStats.getUidTxBytes(uid)); 1006 } 1007 1008 /** 1009 * Convenience class for passing parameters for a finished or cancelled sync to the handler 1010 * to be processed. 1011 */ 1012 private class SyncFinishedOrCancelledMessagePayload { 1013 public final ActiveSyncContext activeSyncContext; 1014 public final SyncResult syncResult; 1015 1016 SyncFinishedOrCancelledMessagePayload(ActiveSyncContext syncContext, 1017 SyncResult syncResult) { 1018 this.activeSyncContext = syncContext; 1019 this.syncResult = syncResult; 1020 } 1021 } 1022 1023 private class UpdatePeriodicSyncMessagePayload { 1024 public final EndPoint target; 1025 public final long pollFrequency; 1026 public final long flex; 1027 public final Bundle extras; 1028 1029 UpdatePeriodicSyncMessagePayload(EndPoint target, long pollFrequency, long flex, 1030 Bundle extras) { 1031 this.target = target; 1032 this.pollFrequency = pollFrequency; 1033 this.flex = flex; 1034 this.extras = extras; 1035 } 1036 } 1037 1038 private void clearBackoffSetting(EndPoint target) { 1039 Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(target); 1040 if (backoff != null && backoff.first == SyncStorageEngine.NOT_IN_BACKOFF_MODE && 1041 backoff.second == SyncStorageEngine.NOT_IN_BACKOFF_MODE) { 1042 return; 1043 } 1044 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1045 Slog.v(TAG, "Clearing backoffs for " + target); 1046 } 1047 mSyncStorageEngine.setBackoff(target, 1048 SyncStorageEngine.NOT_IN_BACKOFF_MODE, 1049 SyncStorageEngine.NOT_IN_BACKOFF_MODE); 1050 1051 rescheduleSyncs(target); 1052 } 1053 1054 private void increaseBackoffSetting(EndPoint target) { 1055 final long now = SystemClock.elapsedRealtime(); 1056 1057 final Pair<Long, Long> previousSettings = 1058 mSyncStorageEngine.getBackoff(target); 1059 long newDelayInMs = -1; 1060 if (previousSettings != null) { 1061 // Don't increase backoff before current backoff is expired. This will happen for op's 1062 // with ignoreBackoff set. 1063 if (now < previousSettings.first) { 1064 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1065 Slog.v(TAG, "Still in backoff, do not increase it. " 1066 + "Remaining: " + ((previousSettings.first - now) / 1000) + " seconds."); 1067 } 1068 return; 1069 } 1070 // Subsequent delays are the double of the previous delay. 1071 newDelayInMs = previousSettings.second * 2; 1072 } 1073 if (newDelayInMs <= 0) { 1074 // The initial delay is the jitterized INITIAL_SYNC_RETRY_TIME_IN_MS. 1075 newDelayInMs = jitterize(INITIAL_SYNC_RETRY_TIME_IN_MS, 1076 (long)(INITIAL_SYNC_RETRY_TIME_IN_MS * 1.1)); 1077 } 1078 1079 // Cap the delay. 1080 long maxSyncRetryTimeInSeconds = Settings.Global.getLong(mContext.getContentResolver(), 1081 Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS, 1082 DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS); 1083 if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) { 1084 newDelayInMs = maxSyncRetryTimeInSeconds * 1000; 1085 } 1086 1087 final long backoff = now + newDelayInMs; 1088 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1089 Slog.v(TAG, "Backoff until: " + backoff + ", delayTime: " + newDelayInMs); 1090 } 1091 mSyncStorageEngine.setBackoff(target, backoff, newDelayInMs); 1092 rescheduleSyncs(target); 1093 } 1094 1095 /** 1096 * Reschedule all scheduled syncs for this EndPoint. The syncs will be scheduled according 1097 * to current backoff and delayUntil values of this EndPoint. 1098 */ 1099 private void rescheduleSyncs(EndPoint target) { 1100 List<SyncOperation> ops = getAllPendingSyncsFromCache(); 1101 int count = 0; 1102 for (SyncOperation op: ops) { 1103 if (!op.isPeriodic && op.target.matchesSpec(target)) { 1104 count++; 1105 removeSyncOperationFromCache(op.jobId); 1106 getJobScheduler().cancel(op.jobId); 1107 postScheduleSyncMessage(op); 1108 } 1109 } 1110 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1111 Slog.v(TAG, "Rescheduled " + count + " syncs for " + target); 1112 } 1113 } 1114 1115 private void setDelayUntilTime(EndPoint target, long delayUntilSeconds) { 1116 final long delayUntil = delayUntilSeconds * 1000; 1117 final long absoluteNow = System.currentTimeMillis(); 1118 long newDelayUntilTime; 1119 if (delayUntil > absoluteNow) { 1120 newDelayUntilTime = SystemClock.elapsedRealtime() + (delayUntil - absoluteNow); 1121 } else { 1122 newDelayUntilTime = 0; 1123 } 1124 mSyncStorageEngine.setDelayUntilTime(target, newDelayUntilTime); 1125 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1126 Slog.v(TAG, "Delay Until time set to " + newDelayUntilTime + " for " + target); 1127 } 1128 rescheduleSyncs(target); 1129 } 1130 1131 private boolean isAdapterDelayed(EndPoint target) { 1132 long now = SystemClock.elapsedRealtime(); 1133 Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(target); 1134 if (backoff != null && backoff.first != SyncStorageEngine.NOT_IN_BACKOFF_MODE 1135 && backoff.first > now) { 1136 return true; 1137 } 1138 if (mSyncStorageEngine.getDelayUntilTime(target) > now) { 1139 return true; 1140 } 1141 return false; 1142 } 1143 1144 /** 1145 * Cancel the active sync if it matches the target. 1146 * @param info object containing info about which syncs to cancel. The target can 1147 * have null account/provider info to specify all accounts/providers. 1148 * @param extras if non-null, specifies the exact sync to remove. 1149 */ 1150 public void cancelActiveSync(SyncStorageEngine.EndPoint info, Bundle extras) { 1151 sendCancelSyncsMessage(info, extras); 1152 } 1153 1154 /** 1155 * Schedule a sync operation with JobScheduler. 1156 */ 1157 private void scheduleSyncOperationH(SyncOperation syncOperation) { 1158 scheduleSyncOperationH(syncOperation, 0L); 1159 } 1160 1161 private void scheduleSyncOperationH(SyncOperation syncOperation, long minDelay) { 1162 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 1163 if (syncOperation == null) { 1164 Slog.e(TAG, "Can't schedule null sync operation."); 1165 return; 1166 } 1167 if (!syncOperation.ignoreBackoff()) { 1168 Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(syncOperation.target); 1169 if (backoff == null) { 1170 Slog.e(TAG, "Couldn't find backoff values for " + syncOperation.target); 1171 backoff = new Pair<Long, Long>(SyncStorageEngine.NOT_IN_BACKOFF_MODE, 1172 SyncStorageEngine.NOT_IN_BACKOFF_MODE); 1173 } 1174 long now = SystemClock.elapsedRealtime(); 1175 long backoffDelay = backoff.first == SyncStorageEngine.NOT_IN_BACKOFF_MODE ? 0 1176 : backoff.first - now; 1177 long delayUntil = mSyncStorageEngine.getDelayUntilTime(syncOperation.target); 1178 long delayUntilDelay = delayUntil > now ? delayUntil - now : 0; 1179 if (isLoggable) { 1180 Slog.v(TAG, "backoff delay:" + backoffDelay 1181 + " delayUntil delay:" + delayUntilDelay); 1182 } 1183 minDelay = Math.max(minDelay, Math.max(backoffDelay, delayUntilDelay)); 1184 } 1185 1186 if (minDelay < 0) { 1187 minDelay = 0; 1188 } 1189 1190 // Check if duplicate syncs are pending. If found, keep one with least expected run time. 1191 if (!syncOperation.isReasonPeriodic()) { 1192 int duplicatesCount = 0; 1193 long now = SystemClock.elapsedRealtime(); 1194 syncOperation.expectedRuntime = now + minDelay; 1195 List<SyncOperation> pending = getAllPendingSyncsFromCache(); 1196 SyncOperation opWithLeastExpectedRuntime = syncOperation; 1197 for (SyncOperation op : pending) { 1198 if (op.isPeriodic) { 1199 continue; 1200 } 1201 if (op.key.equals(syncOperation.key)) { 1202 if (opWithLeastExpectedRuntime.expectedRuntime > op.expectedRuntime) { 1203 opWithLeastExpectedRuntime = op; 1204 } 1205 duplicatesCount++; 1206 } 1207 } 1208 if (duplicatesCount > 1) { 1209 Slog.e(TAG, "FATAL ERROR! File a bug if you see this."); 1210 } 1211 for (SyncOperation op : pending) { 1212 if (op.isPeriodic) { 1213 continue; 1214 } 1215 if (op.key.equals(syncOperation.key)) { 1216 if (op != opWithLeastExpectedRuntime) { 1217 if (isLoggable) { 1218 Slog.v(TAG, "Cancelling duplicate sync " + op); 1219 } 1220 removeSyncOperationFromCache(op.jobId); 1221 getJobScheduler().cancel(op.jobId); 1222 } 1223 } 1224 } 1225 if (opWithLeastExpectedRuntime != syncOperation) { 1226 // Don't schedule because a duplicate sync with earlier expected runtime exists. 1227 if (isLoggable) { 1228 Slog.v(TAG, "Not scheduling because a duplicate exists."); 1229 } 1230 return; 1231 } 1232 } 1233 1234 syncOperation.jobId = getUnusedJobId(); 1235 addSyncOperationToCache(syncOperation); 1236 1237 if (isLoggable) { 1238 Slog.v(TAG, "scheduling sync operation " + syncOperation.target.toString()); 1239 } 1240 1241 // This is done to give preference to syncs that are not pushed back. 1242 int priority = syncOperation.findPriority(); 1243 1244 final int networkType = syncOperation.isNotAllowedOnMetered() ? 1245 JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY; 1246 1247 JobInfo.Builder b = new JobInfo.Builder(syncOperation.jobId, 1248 new ComponentName(mContext, SyncJobService.class)) 1249 .setExtras(syncOperation.toJobInfoExtras()) 1250 .setRequiredNetworkType(networkType) 1251 .setPersisted(true) 1252 .setPriority(priority); 1253 1254 if (syncOperation.isPeriodic) { 1255 b.setPeriodic(syncOperation.periodMillis, syncOperation.flexMillis); 1256 } else { 1257 if (minDelay > 0) { 1258 b.setMinimumLatency(minDelay); 1259 } 1260 getSyncStorageEngine().markPending(syncOperation.target, true); 1261 } 1262 1263 if (syncOperation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING)) { 1264 b.setRequiresCharging(true); 1265 } 1266 1267 getJobScheduler().scheduleAsPackage(b.build(), syncOperation.owningPackage, 1268 syncOperation.target.userId); 1269 } 1270 1271 /** 1272 * Remove scheduled sync operations. 1273 * @param info limit the removals to operations that match this target. The target can 1274 * have null account/provider info to specify all accounts/providers. 1275 */ 1276 public void clearScheduledSyncOperations(SyncStorageEngine.EndPoint info) { 1277 List<SyncOperation> ops = getAllPendingSyncsFromCache(); 1278 for (SyncOperation op: ops) { 1279 if (!op.isPeriodic && op.target.matchesSpec(info)) { 1280 removeSyncOperationFromCache(op.jobId); 1281 getJobScheduler().cancel(op.jobId); 1282 getSyncStorageEngine().markPending(op.target, false); 1283 } 1284 } 1285 mSyncStorageEngine.setBackoff(info, 1286 SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); 1287 } 1288 1289 /** 1290 * Remove a specified sync, if it exists. 1291 * @param info Authority for which the sync is to be removed. 1292 * @param extras extras bundle to uniquely identify sync. 1293 */ 1294 public void cancelScheduledSyncOperation(SyncStorageEngine.EndPoint info, Bundle extras) { 1295 List<SyncOperation> ops = getAllPendingSyncsFromCache(); 1296 for (SyncOperation op: ops) { 1297 if (!op.isPeriodic && op.target.matchesSpec(info) 1298 && syncExtrasEquals(extras, op.extras, false)) { 1299 removeSyncOperationFromCache(op.jobId); 1300 getJobScheduler().cancel(op.jobId); 1301 } 1302 } 1303 setAuthorityPendingState(info); 1304 // Reset the back-off if there are no more syncs pending. 1305 if (!mSyncStorageEngine.isSyncPending(info)) { 1306 mSyncStorageEngine.setBackoff(info, 1307 SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); 1308 } 1309 } 1310 1311 private void maybeRescheduleSync(SyncResult syncResult, SyncOperation operation) { 1312 final boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG); 1313 if (isLoggable) { 1314 Log.d(TAG, "encountered error(s) during the sync: " + syncResult + ", " + operation); 1315 } 1316 1317 // The SYNC_EXTRAS_IGNORE_BACKOFF only applies to the first attempt to sync a given 1318 // request. Retries of the request will always honor the backoff, so clear the 1319 // flag in case we retry this request. 1320 if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) { 1321 operation.extras.remove(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF); 1322 } 1323 1324 if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)) { 1325 if (isLoggable) { 1326 Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified " 1327 + operation); 1328 } 1329 } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false) 1330 && !syncResult.syncAlreadyInProgress) { 1331 // If this was an upward sync then schedule a two-way sync immediately. 1332 operation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD); 1333 if (isLoggable) { 1334 Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync " 1335 + "encountered an error: " + operation); 1336 } 1337 scheduleSyncOperationH(operation, 0 /* immediately */); 1338 } else if (syncResult.tooManyRetries) { 1339 // If this sync aborted because the internal sync loop retried too many times then 1340 // don't reschedule. Otherwise we risk getting into a retry loop. 1341 if (isLoggable) { 1342 Log.d(TAG, "not retrying sync operation because it retried too many times: " 1343 + operation); 1344 } 1345 } else if (syncResult.madeSomeProgress()) { 1346 // If the operation succeeded to some extent then retry immediately. 1347 if (isLoggable) { 1348 Log.d(TAG, "retrying sync operation because even though it had an error " 1349 + "it achieved some success"); 1350 } 1351 scheduleSyncOperationH(operation, 0 /* immediately */); 1352 } else if (syncResult.syncAlreadyInProgress) { 1353 if (isLoggable) { 1354 Log.d(TAG, "retrying sync operation that failed because there was already a " 1355 + "sync in progress: " + operation); 1356 } 1357 scheduleSyncOperationH(operation, DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000); 1358 } else if (syncResult.hasSoftError()) { 1359 // If this was a two-way sync then retry soft errors with an exponential backoff. 1360 if (isLoggable) { 1361 Log.d(TAG, "retrying sync operation because it encountered a soft error: " 1362 + operation); 1363 } 1364 scheduleSyncOperationH(operation); 1365 } else { 1366 // Otherwise do not reschedule. 1367 Log.d(TAG, "not retrying sync operation because the error is a hard error: " 1368 + operation); 1369 } 1370 } 1371 1372 private void onUserUnlocked(int userId) { 1373 // Make sure that accounts we're about to use are valid. 1374 AccountManagerService.getSingleton().validateAccounts(userId); 1375 1376 mSyncAdapters.invalidateCache(userId); 1377 1378 EndPoint target = new EndPoint(null, null, userId); 1379 updateRunningAccounts(target); 1380 1381 // Schedule sync for any accounts under started user. 1382 final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId, 1383 mContext.getOpPackageName()); 1384 for (Account account : accounts) { 1385 scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null, 1386 0 /* no delay */, 0 /* No flexMillis */, 1387 true /* onlyThoseWithUnknownSyncableState */); 1388 } 1389 } 1390 1391 private void onUserStopping(int userId) { 1392 updateRunningAccounts(null /* Don't sync any target */); 1393 1394 cancelActiveSync( 1395 new SyncStorageEngine.EndPoint( 1396 null /* any account */, 1397 null /* any authority */, 1398 userId), 1399 null /* any sync. */ 1400 ); 1401 } 1402 1403 private void onUserRemoved(int userId) { 1404 updateRunningAccounts(null /* Don't sync any target */); 1405 1406 // Clean up the storage engine database 1407 mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId); 1408 List<SyncOperation> ops = getAllPendingSyncsFromCache(); 1409 for (SyncOperation op: ops) { 1410 if (op.target.userId == userId) { 1411 removeSyncOperationFromCache(op.jobId); 1412 getJobScheduler().cancel(op.jobId); 1413 } 1414 } 1415 } 1416 1417 /** 1418 * @hide 1419 */ 1420 class ActiveSyncContext extends ISyncContext.Stub 1421 implements ServiceConnection, IBinder.DeathRecipient { 1422 final SyncOperation mSyncOperation; 1423 final long mHistoryRowId; 1424 ISyncAdapter mSyncAdapter; 1425 final long mStartTime; 1426 long mTimeoutStartTime; 1427 boolean mBound; 1428 final PowerManager.WakeLock mSyncWakeLock; 1429 final int mSyncAdapterUid; 1430 SyncInfo mSyncInfo; 1431 boolean mIsLinkedToDeath = false; 1432 String mEventName; 1433 1434 /** Total bytes transferred, counted at {@link #mLastPolledTimeElapsed} */ 1435 long mBytesTransferredAtLastPoll; 1436 /** 1437 * Last point in {@link SystemClock#elapsedRealtime()} at which we checked the # of bytes 1438 * transferred to/fro by this adapter. 1439 */ 1440 long mLastPolledTimeElapsed; 1441 1442 /** 1443 * Create an ActiveSyncContext for an impending sync and grab the wakelock for that 1444 * sync adapter. Since this grabs the wakelock you need to be sure to call 1445 * close() when you are done with this ActiveSyncContext, whether the sync succeeded 1446 * or not. 1447 * @param syncOperation the SyncOperation we are about to sync 1448 * @param historyRowId the row in which to record the history info for this sync 1449 * @param syncAdapterUid the UID of the application that contains the sync adapter 1450 * for this sync. This is used to attribute the wakelock hold to that application. 1451 */ 1452 public ActiveSyncContext(SyncOperation syncOperation, long historyRowId, 1453 int syncAdapterUid) { 1454 super(); 1455 mSyncAdapterUid = syncAdapterUid; 1456 mSyncOperation = syncOperation; 1457 mHistoryRowId = historyRowId; 1458 mSyncAdapter = null; 1459 mStartTime = SystemClock.elapsedRealtime(); 1460 mTimeoutStartTime = mStartTime; 1461 mSyncWakeLock = mSyncHandler.getSyncWakeLock(mSyncOperation); 1462 mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterUid)); 1463 mSyncWakeLock.acquire(); 1464 } 1465 1466 public void sendHeartbeat() { 1467 // Heartbeats are no longer used. 1468 } 1469 1470 public void onFinished(SyncResult result) { 1471 if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "onFinished: " + this); 1472 // Include "this" in the message so that the handler can ignore it if this 1473 // ActiveSyncContext is no longer the mActiveSyncContext at message handling 1474 // time. 1475 sendSyncFinishedOrCanceledMessage(this, result); 1476 } 1477 1478 public void toString(StringBuilder sb) { 1479 sb.append("startTime ").append(mStartTime) 1480 .append(", mTimeoutStartTime ").append(mTimeoutStartTime) 1481 .append(", mHistoryRowId ").append(mHistoryRowId) 1482 .append(", syncOperation ").append(mSyncOperation); 1483 } 1484 1485 public void onServiceConnected(ComponentName name, IBinder service) { 1486 Message msg = mSyncHandler.obtainMessage(); 1487 msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED; 1488 msg.obj = new ServiceConnectionData(this, service); 1489 mSyncHandler.sendMessage(msg); 1490 } 1491 1492 public void onServiceDisconnected(ComponentName name) { 1493 Message msg = mSyncHandler.obtainMessage(); 1494 msg.what = SyncHandler.MESSAGE_SERVICE_DISCONNECTED; 1495 msg.obj = new ServiceConnectionData(this, null); 1496 mSyncHandler.sendMessage(msg); 1497 } 1498 1499 boolean bindToSyncAdapter(ComponentName serviceComponent, int userId) { 1500 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1501 Log.d(TAG, "bindToSyncAdapter: " + serviceComponent + ", connection " + this); 1502 } 1503 Intent intent = new Intent(); 1504 intent.setAction("android.content.SyncAdapter"); 1505 intent.setComponent(serviceComponent); 1506 intent.putExtra(Intent.EXTRA_CLIENT_LABEL, 1507 com.android.internal.R.string.sync_binding_label); 1508 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser( 1509 mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0, 1510 null, new UserHandle(userId))); 1511 mBound = true; 1512 final boolean bindResult = mContext.bindServiceAsUser(intent, this, 1513 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND 1514 | Context.BIND_ALLOW_OOM_MANAGEMENT, 1515 new UserHandle(mSyncOperation.target.userId)); 1516 if (!bindResult) { 1517 mBound = false; 1518 } else { 1519 try { 1520 mEventName = mSyncOperation.wakeLockName(); 1521 mBatteryStats.noteSyncStart(mEventName, mSyncAdapterUid); 1522 } catch (RemoteException e) { 1523 } 1524 } 1525 return bindResult; 1526 } 1527 1528 /** 1529 * Performs the required cleanup, which is the releasing of the wakelock and 1530 * unbinding from the sync adapter (if actually bound). 1531 */ 1532 protected void close() { 1533 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1534 Log.d(TAG, "unBindFromSyncAdapter: connection " + this); 1535 } 1536 if (mBound) { 1537 mBound = false; 1538 mContext.unbindService(this); 1539 try { 1540 mBatteryStats.noteSyncFinish(mEventName, mSyncAdapterUid); 1541 } catch (RemoteException e) { 1542 } 1543 } 1544 mSyncWakeLock.release(); 1545 mSyncWakeLock.setWorkSource(null); 1546 } 1547 1548 public String toString() { 1549 StringBuilder sb = new StringBuilder(); 1550 toString(sb); 1551 return sb.toString(); 1552 } 1553 1554 @Override 1555 public void binderDied() { 1556 sendSyncFinishedOrCanceledMessage(this, null); 1557 } 1558 } 1559 1560 protected void dump(FileDescriptor fd, PrintWriter pw) { 1561 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 1562 dumpPendingSyncs(pw); 1563 dumpPeriodicSyncs(pw); 1564 dumpSyncState(ipw); 1565 dumpSyncHistory(ipw); 1566 dumpSyncAdapters(ipw); 1567 } 1568 1569 static String formatTime(long time) { 1570 Time tobj = new Time(); 1571 tobj.set(time); 1572 return tobj.format("%Y-%m-%d %H:%M:%S"); 1573 } 1574 1575 protected void dumpPendingSyncs(PrintWriter pw) { 1576 pw.println("Pending Syncs:"); 1577 List<SyncOperation> pendingSyncs = getAllPendingSyncsFromCache(); 1578 int count = 0; 1579 for (SyncOperation op: pendingSyncs) { 1580 if (!op.isPeriodic) { 1581 pw.println(op.dump(null, false)); 1582 count++; 1583 } 1584 } 1585 pw.println("Total: " + count); 1586 pw.println(); 1587 } 1588 1589 protected void dumpPeriodicSyncs(PrintWriter pw) { 1590 pw.println("Periodic Syncs:"); 1591 List<SyncOperation> pendingSyncs = getAllPendingSyncsFromCache(); 1592 int count = 0; 1593 for (SyncOperation op: pendingSyncs) { 1594 if (op.isPeriodic) { 1595 pw.println(op.dump(null, false)); 1596 count++; 1597 } 1598 } 1599 pw.println("Total: " + count); 1600 pw.println(); 1601 } 1602 1603 protected void dumpSyncState(PrintWriter pw) { 1604 pw.print("data connected: "); pw.println(mDataConnectionIsConnected); 1605 pw.print("auto sync: "); 1606 List<UserInfo> users = getAllUsers(); 1607 if (users != null) { 1608 for (UserInfo user : users) { 1609 pw.print("u" + user.id + "=" 1610 + mSyncStorageEngine.getMasterSyncAutomatically(user.id) + " "); 1611 } 1612 pw.println(); 1613 } 1614 pw.print("memory low: "); pw.println(mStorageIsLow); 1615 pw.print("device idle: "); pw.println(mDeviceIsIdle); 1616 pw.print("reported active: "); pw.println(mReportedSyncActive); 1617 1618 final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts(); 1619 1620 pw.print("accounts: "); 1621 if (accounts != INITIAL_ACCOUNTS_ARRAY) { 1622 pw.println(accounts.length); 1623 } else { 1624 pw.println("not known yet"); 1625 } 1626 final long now = SystemClock.elapsedRealtime(); 1627 pw.print("now: "); pw.print(now); 1628 pw.println(" (" + formatTime(System.currentTimeMillis()) + ")"); 1629 pw.println(" (HH:MM:SS)"); 1630 pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now / 1000)); 1631 pw.println(" (HH:MM:SS)"); 1632 pw.print("time spent syncing: "); 1633 pw.print(DateUtils.formatElapsedTime( 1634 mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000)); 1635 pw.print(" (HH:MM:SS), sync "); 1636 pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not "); 1637 pw.println("in progress"); 1638 1639 pw.println(); 1640 pw.println("Active Syncs: " + mActiveSyncContexts.size()); 1641 final PackageManager pm = mContext.getPackageManager(); 1642 for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) { 1643 final long durationInSeconds = (now - activeSyncContext.mStartTime) / 1000; 1644 pw.print(" "); 1645 pw.print(DateUtils.formatElapsedTime(durationInSeconds)); 1646 pw.print(" - "); 1647 pw.print(activeSyncContext.mSyncOperation.dump(pm, false)); 1648 pw.println(); 1649 } 1650 1651 // Join the installed sync adapter with the accounts list and emit for everything. 1652 pw.println(); 1653 pw.println("Sync Status"); 1654 for (AccountAndUser account : accounts) { 1655 pw.printf("Account %s u%d %s\n", 1656 account.account.name, account.userId, account.account.type); 1657 1658 pw.println("======================================================================="); 1659 final PrintTable table = new PrintTable(12); 1660 table.set(0, 0, 1661 "Authority", // 0 1662 "Syncable", // 1 1663 "Enabled", // 2 1664 "Delay", // 3 1665 "Loc", // 4 1666 "Poll", // 5 1667 "Per", // 6 1668 "Serv", // 7 1669 "User", // 8 1670 "Tot", // 9 1671 "Time", // 10 1672 "Last Sync" // 11 1673 ); 1674 1675 final List<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> sorted = 1676 Lists.newArrayList(); 1677 sorted.addAll(mSyncAdapters.getAllServices(account.userId)); 1678 Collections.sort(sorted, 1679 new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() { 1680 @Override 1681 public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs, 1682 RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) { 1683 return lhs.type.authority.compareTo(rhs.type.authority); 1684 } 1685 }); 1686 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : sorted) { 1687 if (!syncAdapterType.type.accountType.equals(account.account.type)) { 1688 continue; 1689 } 1690 int row = table.getNumRows(); 1691 Pair<AuthorityInfo, SyncStatusInfo> syncAuthoritySyncStatus = 1692 mSyncStorageEngine.getCopyOfAuthorityWithSyncStatus( 1693 new SyncStorageEngine.EndPoint( 1694 account.account, 1695 syncAdapterType.type.authority, 1696 account.userId)); 1697 SyncStorageEngine.AuthorityInfo settings = syncAuthoritySyncStatus.first; 1698 SyncStatusInfo status = syncAuthoritySyncStatus.second; 1699 String authority = settings.target.provider; 1700 if (authority.length() > 50) { 1701 authority = authority.substring(authority.length() - 50); 1702 } 1703 table.set(row, 0, authority, settings.syncable, settings.enabled); 1704 table.set(row, 4, 1705 status.numSourceLocal, 1706 status.numSourcePoll, 1707 status.numSourcePeriodic, 1708 status.numSourceServer, 1709 status.numSourceUser, 1710 status.numSyncs, 1711 DateUtils.formatElapsedTime(status.totalElapsedTime / 1000)); 1712 1713 int row1 = row; 1714 if (settings.delayUntil > now) { 1715 table.set(row1++, 12, "D: " + (settings.delayUntil - now) / 1000); 1716 if (settings.backoffTime > now) { 1717 table.set(row1++, 12, "B: " + (settings.backoffTime - now) / 1000); 1718 table.set(row1++, 12, settings.backoffDelay / 1000); 1719 } 1720 } 1721 1722 if (status.lastSuccessTime != 0) { 1723 table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastSuccessSource] 1724 + " " + "SUCCESS"); 1725 table.set(row1++, 11, formatTime(status.lastSuccessTime)); 1726 } 1727 if (status.lastFailureTime != 0) { 1728 table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastFailureSource] 1729 + " " + "FAILURE"); 1730 table.set(row1++, 11, formatTime(status.lastFailureTime)); 1731 //noinspection UnusedAssignment 1732 table.set(row1++, 11, status.lastFailureMesg); 1733 } 1734 } 1735 table.writeTo(pw); 1736 } 1737 } 1738 1739 private void dumpTimeSec(PrintWriter pw, long time) { 1740 pw.print(time/1000); pw.print('.'); pw.print((time/100)%10); 1741 pw.print('s'); 1742 } 1743 1744 private void dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds) { 1745 pw.print("Success ("); pw.print(ds.successCount); 1746 if (ds.successCount > 0) { 1747 pw.print(" for "); dumpTimeSec(pw, ds.successTime); 1748 pw.print(" avg="); dumpTimeSec(pw, ds.successTime/ds.successCount); 1749 } 1750 pw.print(") Failure ("); pw.print(ds.failureCount); 1751 if (ds.failureCount > 0) { 1752 pw.print(" for "); dumpTimeSec(pw, ds.failureTime); 1753 pw.print(" avg="); dumpTimeSec(pw, ds.failureTime/ds.failureCount); 1754 } 1755 pw.println(")"); 1756 } 1757 1758 protected void dumpSyncHistory(PrintWriter pw) { 1759 dumpRecentHistory(pw); 1760 dumpDayStatistics(pw); 1761 } 1762 1763 private void dumpRecentHistory(PrintWriter pw) { 1764 final ArrayList<SyncStorageEngine.SyncHistoryItem> items 1765 = mSyncStorageEngine.getSyncHistory(); 1766 if (items != null && items.size() > 0) { 1767 final Map<String, AuthoritySyncStats> authorityMap = Maps.newHashMap(); 1768 long totalElapsedTime = 0; 1769 long totalTimes = 0; 1770 final int N = items.size(); 1771 1772 int maxAuthority = 0; 1773 int maxAccount = 0; 1774 for (SyncStorageEngine.SyncHistoryItem item : items) { 1775 SyncStorageEngine.AuthorityInfo authorityInfo 1776 = mSyncStorageEngine.getAuthority(item.authorityId); 1777 final String authorityName; 1778 final String accountKey; 1779 if (authorityInfo != null) { 1780 authorityName = authorityInfo.target.provider; 1781 accountKey = authorityInfo.target.account.name + "/" 1782 + authorityInfo.target.account.type 1783 + " u" + authorityInfo.target.userId; 1784 } else { 1785 authorityName = "Unknown"; 1786 accountKey = "Unknown"; 1787 } 1788 1789 int length = authorityName.length(); 1790 if (length > maxAuthority) { 1791 maxAuthority = length; 1792 } 1793 length = accountKey.length(); 1794 if (length > maxAccount) { 1795 maxAccount = length; 1796 } 1797 1798 final long elapsedTime = item.elapsedTime; 1799 totalElapsedTime += elapsedTime; 1800 totalTimes++; 1801 AuthoritySyncStats authoritySyncStats = authorityMap.get(authorityName); 1802 if (authoritySyncStats == null) { 1803 authoritySyncStats = new AuthoritySyncStats(authorityName); 1804 authorityMap.put(authorityName, authoritySyncStats); 1805 } 1806 authoritySyncStats.elapsedTime += elapsedTime; 1807 authoritySyncStats.times++; 1808 final Map<String, AccountSyncStats> accountMap = authoritySyncStats.accountMap; 1809 AccountSyncStats accountSyncStats = accountMap.get(accountKey); 1810 if (accountSyncStats == null) { 1811 accountSyncStats = new AccountSyncStats(accountKey); 1812 accountMap.put(accountKey, accountSyncStats); 1813 } 1814 accountSyncStats.elapsedTime += elapsedTime; 1815 accountSyncStats.times++; 1816 1817 } 1818 1819 if (totalElapsedTime > 0) { 1820 pw.println(); 1821 pw.printf("Detailed Statistics (Recent history): " 1822 + "%d (# of times) %ds (sync time)\n", 1823 totalTimes, totalElapsedTime / 1000); 1824 1825 final List<AuthoritySyncStats> sortedAuthorities = 1826 new ArrayList<AuthoritySyncStats>(authorityMap.values()); 1827 Collections.sort(sortedAuthorities, new Comparator<AuthoritySyncStats>() { 1828 @Override 1829 public int compare(AuthoritySyncStats lhs, AuthoritySyncStats rhs) { 1830 // reverse order 1831 int compare = Integer.compare(rhs.times, lhs.times); 1832 if (compare == 0) { 1833 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime); 1834 } 1835 return compare; 1836 } 1837 }); 1838 1839 final int maxLength = Math.max(maxAuthority, maxAccount + 3); 1840 final int padLength = 2 + 2 + maxLength + 2 + 10 + 11; 1841 final char chars[] = new char[padLength]; 1842 Arrays.fill(chars, '-'); 1843 final String separator = new String(chars); 1844 1845 final String authorityFormat = 1846 String.format(" %%-%ds: %%-9s %%-11s\n", maxLength + 2); 1847 final String accountFormat = 1848 String.format(" %%-%ds: %%-9s %%-11s\n", maxLength); 1849 1850 pw.println(separator); 1851 for (AuthoritySyncStats authoritySyncStats : sortedAuthorities) { 1852 String name = authoritySyncStats.name; 1853 long elapsedTime; 1854 int times; 1855 String timeStr; 1856 String timesStr; 1857 1858 elapsedTime = authoritySyncStats.elapsedTime; 1859 times = authoritySyncStats.times; 1860 timeStr = String.format("%ds/%d%%", 1861 elapsedTime / 1000, 1862 elapsedTime * 100 / totalElapsedTime); 1863 timesStr = String.format("%d/%d%%", 1864 times, 1865 times * 100 / totalTimes); 1866 pw.printf(authorityFormat, name, timesStr, timeStr); 1867 1868 final List<AccountSyncStats> sortedAccounts = 1869 new ArrayList<AccountSyncStats>( 1870 authoritySyncStats.accountMap.values()); 1871 Collections.sort(sortedAccounts, new Comparator<AccountSyncStats>() { 1872 @Override 1873 public int compare(AccountSyncStats lhs, AccountSyncStats rhs) { 1874 // reverse order 1875 int compare = Integer.compare(rhs.times, lhs.times); 1876 if (compare == 0) { 1877 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime); 1878 } 1879 return compare; 1880 } 1881 }); 1882 for (AccountSyncStats stats: sortedAccounts) { 1883 elapsedTime = stats.elapsedTime; 1884 times = stats.times; 1885 timeStr = String.format("%ds/%d%%", 1886 elapsedTime / 1000, 1887 elapsedTime * 100 / totalElapsedTime); 1888 timesStr = String.format("%d/%d%%", 1889 times, 1890 times * 100 / totalTimes); 1891 pw.printf(accountFormat, stats.name, timesStr, timeStr); 1892 } 1893 pw.println(separator); 1894 } 1895 } 1896 1897 pw.println(); 1898 pw.println("Recent Sync History"); 1899 final String format = " %-" + maxAccount + "s %-" + maxAuthority + "s %s\n"; 1900 final Map<String, Long> lastTimeMap = Maps.newHashMap(); 1901 final PackageManager pm = mContext.getPackageManager(); 1902 for (int i = 0; i < N; i++) { 1903 SyncStorageEngine.SyncHistoryItem item = items.get(i); 1904 SyncStorageEngine.AuthorityInfo authorityInfo 1905 = mSyncStorageEngine.getAuthority(item.authorityId); 1906 final String authorityName; 1907 final String accountKey; 1908 if (authorityInfo != null) { 1909 authorityName = authorityInfo.target.provider; 1910 accountKey = authorityInfo.target.account.name + "/" 1911 + authorityInfo.target.account.type 1912 + " u" + authorityInfo.target.userId; 1913 } else { 1914 authorityName = "Unknown"; 1915 accountKey = "Unknown"; 1916 } 1917 final long elapsedTime = item.elapsedTime; 1918 final Time time = new Time(); 1919 final long eventTime = item.eventTime; 1920 time.set(eventTime); 1921 1922 final String key = authorityName + "/" + accountKey; 1923 final Long lastEventTime = lastTimeMap.get(key); 1924 final String diffString; 1925 if (lastEventTime == null) { 1926 diffString = ""; 1927 } else { 1928 final long diff = (lastEventTime - eventTime) / 1000; 1929 if (diff < 60) { 1930 diffString = String.valueOf(diff); 1931 } else if (diff < 3600) { 1932 diffString = String.format("%02d:%02d", diff / 60, diff % 60); 1933 } else { 1934 final long sec = diff % 3600; 1935 diffString = String.format("%02d:%02d:%02d", 1936 diff / 3600, sec / 60, sec % 60); 1937 } 1938 } 1939 lastTimeMap.put(key, eventTime); 1940 1941 pw.printf(" #%-3d: %s %8s %5.1fs %8s", 1942 i + 1, 1943 formatTime(eventTime), 1944 SyncStorageEngine.SOURCES[item.source], 1945 ((float) elapsedTime) / 1000, 1946 diffString); 1947 pw.printf(format, accountKey, authorityName, 1948 SyncOperation.reasonToString(pm, item.reason)); 1949 1950 if (item.event != SyncStorageEngine.EVENT_STOP 1951 || item.upstreamActivity != 0 1952 || item.downstreamActivity != 0) { 1953 pw.printf(" event=%d upstreamActivity=%d downstreamActivity=%d\n", 1954 item.event, 1955 item.upstreamActivity, 1956 item.downstreamActivity); 1957 } 1958 if (item.mesg != null 1959 && !SyncStorageEngine.MESG_SUCCESS.equals(item.mesg)) { 1960 pw.printf(" mesg=%s\n", item.mesg); 1961 } 1962 } 1963 pw.println(); 1964 pw.println("Recent Sync History Extras"); 1965 for (int i = 0; i < N; i++) { 1966 final SyncStorageEngine.SyncHistoryItem item = items.get(i); 1967 final Bundle extras = item.extras; 1968 if (extras == null || extras.size() == 0) { 1969 continue; 1970 } 1971 final SyncStorageEngine.AuthorityInfo authorityInfo 1972 = mSyncStorageEngine.getAuthority(item.authorityId); 1973 final String authorityName; 1974 final String accountKey; 1975 if (authorityInfo != null) { 1976 authorityName = authorityInfo.target.provider; 1977 accountKey = authorityInfo.target.account.name + "/" 1978 + authorityInfo.target.account.type 1979 + " u" + authorityInfo.target.userId; 1980 } else { 1981 authorityName = "Unknown"; 1982 accountKey = "Unknown"; 1983 } 1984 final Time time = new Time(); 1985 final long eventTime = item.eventTime; 1986 time.set(eventTime); 1987 1988 pw.printf(" #%-3d: %s %8s ", 1989 i + 1, 1990 formatTime(eventTime), 1991 SyncStorageEngine.SOURCES[item.source]); 1992 1993 pw.printf(format, accountKey, authorityName, extras); 1994 } 1995 } 1996 } 1997 1998 private void dumpDayStatistics(PrintWriter pw) { 1999 SyncStorageEngine.DayStats dses[] = mSyncStorageEngine.getDayStatistics(); 2000 if (dses != null && dses[0] != null) { 2001 pw.println(); 2002 pw.println("Sync Statistics"); 2003 pw.print(" Today: "); dumpDayStatistic(pw, dses[0]); 2004 int today = dses[0].day; 2005 int i; 2006 SyncStorageEngine.DayStats ds; 2007 2008 // Print each day in the current week. 2009 for (i=1; i<=6 && i < dses.length; i++) { 2010 ds = dses[i]; 2011 if (ds == null) break; 2012 int delta = today-ds.day; 2013 if (delta > 6) break; 2014 2015 pw.print(" Day-"); pw.print(delta); pw.print(": "); 2016 dumpDayStatistic(pw, ds); 2017 } 2018 2019 // Aggregate all following days into weeks and print totals. 2020 int weekDay = today; 2021 while (i < dses.length) { 2022 SyncStorageEngine.DayStats aggr = null; 2023 weekDay -= 7; 2024 while (i < dses.length) { 2025 ds = dses[i]; 2026 if (ds == null) { 2027 i = dses.length; 2028 break; 2029 } 2030 int delta = weekDay-ds.day; 2031 if (delta > 6) break; 2032 i++; 2033 2034 if (aggr == null) { 2035 aggr = new SyncStorageEngine.DayStats(weekDay); 2036 } 2037 aggr.successCount += ds.successCount; 2038 aggr.successTime += ds.successTime; 2039 aggr.failureCount += ds.failureCount; 2040 aggr.failureTime += ds.failureTime; 2041 } 2042 if (aggr != null) { 2043 pw.print(" Week-"); pw.print((today-weekDay)/7); pw.print(": "); 2044 dumpDayStatistic(pw, aggr); 2045 } 2046 } 2047 } 2048 } 2049 2050 private void dumpSyncAdapters(IndentingPrintWriter pw) { 2051 pw.println(); 2052 final List<UserInfo> users = getAllUsers(); 2053 if (users != null) { 2054 for (UserInfo user : users) { 2055 pw.println("Sync adapters for " + user + ":"); 2056 pw.increaseIndent(); 2057 for (RegisteredServicesCache.ServiceInfo<?> info : 2058 mSyncAdapters.getAllServices(user.id)) { 2059 pw.println(info); 2060 } 2061 pw.decreaseIndent(); 2062 pw.println(); 2063 } 2064 } 2065 } 2066 2067 private static class AuthoritySyncStats { 2068 String name; 2069 long elapsedTime; 2070 int times; 2071 Map<String, AccountSyncStats> accountMap = Maps.newHashMap(); 2072 2073 private AuthoritySyncStats(String name) { 2074 this.name = name; 2075 } 2076 } 2077 2078 private static class AccountSyncStats { 2079 String name; 2080 long elapsedTime; 2081 int times; 2082 2083 private AccountSyncStats(String name) { 2084 this.name = name; 2085 } 2086 } 2087 2088 /** 2089 * A helper object to keep track of the time we have spent syncing since the last boot 2090 */ 2091 private class SyncTimeTracker { 2092 /** True if a sync was in progress on the most recent call to update() */ 2093 boolean mLastWasSyncing = false; 2094 /** Used to track when lastWasSyncing was last set */ 2095 long mWhenSyncStarted = 0; 2096 /** The cumulative time we have spent syncing */ 2097 private long mTimeSpentSyncing; 2098 2099 /** Call to let the tracker know that the sync state may have changed */ 2100 public synchronized void update() { 2101 final boolean isSyncInProgress = !mActiveSyncContexts.isEmpty(); 2102 if (isSyncInProgress == mLastWasSyncing) return; 2103 final long now = SystemClock.elapsedRealtime(); 2104 if (isSyncInProgress) { 2105 mWhenSyncStarted = now; 2106 } else { 2107 mTimeSpentSyncing += now - mWhenSyncStarted; 2108 } 2109 mLastWasSyncing = isSyncInProgress; 2110 } 2111 2112 /** Get how long we have been syncing, in ms */ 2113 public synchronized long timeSpentSyncing() { 2114 if (!mLastWasSyncing) return mTimeSpentSyncing; 2115 2116 final long now = SystemClock.elapsedRealtime(); 2117 return mTimeSpentSyncing + (now - mWhenSyncStarted); 2118 } 2119 } 2120 2121 class ServiceConnectionData { 2122 public final ActiveSyncContext activeSyncContext; 2123 public final IBinder adapter; 2124 2125 ServiceConnectionData(ActiveSyncContext activeSyncContext, IBinder adapter) { 2126 this.activeSyncContext = activeSyncContext; 2127 this.adapter = adapter; 2128 } 2129 } 2130 2131 /** 2132 * Handles SyncOperation Messages that are posted to the associated 2133 * HandlerThread. 2134 */ 2135 class SyncHandler extends Handler { 2136 // Messages that can be sent on mHandler. 2137 private static final int MESSAGE_SYNC_FINISHED = 1; 2138 private static final int MESSAGE_RELEASE_MESSAGES_FROM_QUEUE = 2; 2139 private static final int MESSAGE_SERVICE_CONNECTED = 4; 2140 private static final int MESSAGE_SERVICE_DISCONNECTED = 5; 2141 private static final int MESSAGE_CANCEL = 6; 2142 static final int MESSAGE_JOBSERVICE_OBJECT = 7; 2143 static final int MESSAGE_START_SYNC = 10; 2144 static final int MESSAGE_STOP_SYNC = 11; 2145 static final int MESSAGE_SCHEDULE_SYNC = 12; 2146 static final int MESSAGE_UPDATE_PERIODIC_SYNC = 13; 2147 static final int MESSAGE_REMOVE_PERIODIC_SYNC = 14; 2148 /** 2149 * Posted delayed in order to expire syncs that are long-running. 2150 * obj: {@link com.android.server.content.SyncManager.ActiveSyncContext} 2151 */ 2152 private static final int MESSAGE_SYNC_EXPIRED = 7; 2153 /** 2154 * Posted periodically to monitor network process for long-running syncs. 2155 * obj: {@link com.android.server.content.SyncManager.ActiveSyncContext} 2156 */ 2157 private static final int MESSAGE_MONITOR_SYNC = 8; 2158 private static final int MESSAGE_ACCOUNTS_UPDATED = 9; 2159 2160 public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker(); 2161 private final HashMap<String, PowerManager.WakeLock> mWakeLocks = Maps.newHashMap(); 2162 2163 private List<Message> mUnreadyQueue = new ArrayList<Message>(); 2164 2165 void onBootCompleted() { 2166 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2167 Slog.v(TAG, "Boot completed."); 2168 } 2169 checkIfDeviceReady(); 2170 } 2171 2172 void onDeviceProvisioned() { 2173 if (Log.isLoggable(TAG, Log.DEBUG)) { 2174 Log.d(TAG, "mProvisioned=" + mProvisioned); 2175 } 2176 checkIfDeviceReady(); 2177 } 2178 2179 void checkIfDeviceReady() { 2180 if (mProvisioned && mBootCompleted) { 2181 synchronized(this) { 2182 // Dispatch any stashed messages. 2183 obtainMessage(MESSAGE_RELEASE_MESSAGES_FROM_QUEUE).sendToTarget(); 2184 } 2185 } 2186 } 2187 2188 /** 2189 * Stash any messages that come to the handler before boot is complete or before the device 2190 * is properly provisioned (i.e. out of set-up wizard). 2191 * {@link #onBootCompleted()} and {@link SyncHandler#onDeviceProvisioned} both 2192 * need to come in before we start syncing. 2193 * @param msg Message to dispatch at a later point. 2194 * @return true if a message was enqueued, false otherwise. This is to avoid losing the 2195 * message if we manage to acquire the lock but by the time we do boot has completed. 2196 */ 2197 private boolean tryEnqueueMessageUntilReadyToRun(Message msg) { 2198 synchronized (this) { 2199 if (!mBootCompleted || !mProvisioned) { 2200 if (msg.what == MESSAGE_START_SYNC) { 2201 deferSyncH((SyncOperation) msg.obj, 60*1000 /* 1 minute */); 2202 } 2203 // Need to copy the message bc looper will recycle it. 2204 Message m = Message.obtain(msg); 2205 mUnreadyQueue.add(m); 2206 return true; 2207 } else { 2208 return false; 2209 } 2210 } 2211 } 2212 2213 public SyncHandler(Looper looper) { 2214 super(looper); 2215 } 2216 2217 public void handleMessage(Message msg) { 2218 try { 2219 mSyncManagerWakeLock.acquire(); 2220 // We only want to enqueue sync related messages until device is ready. 2221 // Other messages are handled without enqueuing. 2222 if (msg.what == MESSAGE_JOBSERVICE_OBJECT) { 2223 Slog.i(TAG, "Got SyncJobService instance."); 2224 mSyncJobService = (SyncJobService) msg.obj; 2225 } else if (msg.what == SyncHandler.MESSAGE_ACCOUNTS_UPDATED) { 2226 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2227 Slog.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED"); 2228 } 2229 EndPoint targets = (EndPoint) msg.obj; 2230 updateRunningAccountsH(targets); 2231 } else if (msg.what == MESSAGE_RELEASE_MESSAGES_FROM_QUEUE) { 2232 if (mUnreadyQueue != null) { 2233 for (Message m : mUnreadyQueue) { 2234 handleSyncMessage(m); 2235 } 2236 mUnreadyQueue = null; 2237 } 2238 } else if (tryEnqueueMessageUntilReadyToRun(msg)) { 2239 // No work to be done. 2240 } else { 2241 handleSyncMessage(msg); 2242 } 2243 } finally { 2244 mSyncManagerWakeLock.release(); 2245 } 2246 } 2247 2248 private void handleSyncMessage(Message msg) { 2249 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2250 2251 try { 2252 mDataConnectionIsConnected = readDataConnectionState(); 2253 switch (msg.what) { 2254 case MESSAGE_SCHEDULE_SYNC: 2255 SyncOperation op = (SyncOperation) msg.obj; 2256 scheduleSyncOperationH(op); 2257 break; 2258 2259 case MESSAGE_START_SYNC: 2260 op = (SyncOperation) msg.obj; 2261 startSyncH(op); 2262 break; 2263 2264 case MESSAGE_STOP_SYNC: 2265 op = (SyncOperation) msg.obj; 2266 boolean reschedule = msg.arg1 != 0; 2267 boolean applyBackoff = msg.arg2 != 0; 2268 if (isLoggable) { 2269 Slog.v(TAG, "Stop sync received. Reschedule: " + reschedule 2270 + "Backoff: " + applyBackoff); 2271 } 2272 if (applyBackoff) { 2273 increaseBackoffSetting(op.target); 2274 } 2275 if (reschedule) { 2276 scheduleSyncOperationH(op); 2277 } 2278 ActiveSyncContext asc = findActiveSyncContextH(op.jobId); 2279 if (asc != null) { 2280 runSyncFinishedOrCanceledH(null /* no result */, asc); 2281 } 2282 break; 2283 2284 case MESSAGE_UPDATE_PERIODIC_SYNC: 2285 UpdatePeriodicSyncMessagePayload data = 2286 (UpdatePeriodicSyncMessagePayload) msg.obj; 2287 updateOrAddPeriodicSyncH(data.target, data.pollFrequency, 2288 data.flex, data.extras); 2289 break; 2290 case MESSAGE_REMOVE_PERIODIC_SYNC: 2291 removePeriodicSyncH((EndPoint)msg.obj, msg.getData()); 2292 break; 2293 2294 case SyncHandler.MESSAGE_CANCEL: 2295 SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj; 2296 Bundle extras = msg.peekData(); 2297 if (Log.isLoggable(TAG, Log.DEBUG)) { 2298 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_CANCEL: " 2299 + endpoint + " bundle: " + extras); 2300 } 2301 cancelActiveSyncH(endpoint, extras); 2302 break; 2303 2304 case SyncHandler.MESSAGE_SYNC_FINISHED: 2305 SyncFinishedOrCancelledMessagePayload payload = 2306 (SyncFinishedOrCancelledMessagePayload) msg.obj; 2307 if (!isSyncStillActiveH(payload.activeSyncContext)) { 2308 Log.d(TAG, "handleSyncHandlerMessage: dropping since the " 2309 + "sync is no longer active: " 2310 + payload.activeSyncContext); 2311 break; 2312 } 2313 if (isLoggable) { 2314 Slog.v(TAG, "syncFinished" + payload.activeSyncContext.mSyncOperation); 2315 } 2316 mSyncJobService.callJobFinished( 2317 payload.activeSyncContext.mSyncOperation.jobId, false); 2318 runSyncFinishedOrCanceledH(payload.syncResult, 2319 payload.activeSyncContext); 2320 break; 2321 2322 case SyncHandler.MESSAGE_SERVICE_CONNECTED: { 2323 ServiceConnectionData msgData = (ServiceConnectionData) msg.obj; 2324 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2325 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: " 2326 + msgData.activeSyncContext); 2327 } 2328 // Check that this isn't an old message. 2329 if (isSyncStillActiveH(msgData.activeSyncContext)) { 2330 runBoundToAdapterH( 2331 msgData.activeSyncContext, 2332 msgData.adapter); 2333 } 2334 break; 2335 } 2336 2337 case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: { 2338 final ActiveSyncContext currentSyncContext = 2339 ((ServiceConnectionData) msg.obj).activeSyncContext; 2340 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2341 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: " 2342 + currentSyncContext); 2343 } 2344 // Check that this isn't an old message. 2345 if (isSyncStillActiveH(currentSyncContext)) { 2346 // cancel the sync if we have a syncadapter, which means one is 2347 // outstanding 2348 try { 2349 if (currentSyncContext.mSyncAdapter != null) { 2350 currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext); 2351 } 2352 } catch (RemoteException e) { 2353 // We don't need to retry this in this case. 2354 } 2355 2356 // Pretend that the sync failed with an IOException, 2357 // which is a soft error. 2358 SyncResult syncResult = new SyncResult(); 2359 syncResult.stats.numIoExceptions++; 2360 mSyncJobService.callJobFinished( 2361 currentSyncContext.mSyncOperation.jobId, false); 2362 runSyncFinishedOrCanceledH(syncResult, currentSyncContext); 2363 } 2364 break; 2365 } 2366 2367 case SyncHandler.MESSAGE_MONITOR_SYNC: 2368 ActiveSyncContext monitoredSyncContext = (ActiveSyncContext) msg.obj; 2369 if (Log.isLoggable(TAG, Log.DEBUG)) { 2370 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_MONITOR_SYNC: " + 2371 monitoredSyncContext.mSyncOperation.target); 2372 } 2373 2374 if (isSyncNotUsingNetworkH(monitoredSyncContext)) { 2375 Log.w(TAG, String.format( 2376 "Detected sync making no progress for %s. cancelling.", 2377 monitoredSyncContext)); 2378 mSyncJobService.callJobFinished( 2379 monitoredSyncContext.mSyncOperation.jobId, false); 2380 runSyncFinishedOrCanceledH( 2381 null /* cancel => no result */, monitoredSyncContext); 2382 } else { 2383 // Repost message to check again. 2384 postMonitorSyncProgressMessage(monitoredSyncContext); 2385 } 2386 break; 2387 2388 } 2389 } finally { 2390 mSyncTimeTracker.update(); 2391 } 2392 } 2393 2394 private PowerManager.WakeLock getSyncWakeLock(SyncOperation operation) { 2395 final String wakeLockKey = operation.wakeLockName(); 2396 PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey); 2397 if (wakeLock == null) { 2398 final String name = SYNC_WAKE_LOCK_PREFIX + wakeLockKey; 2399 wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name); 2400 wakeLock.setReferenceCounted(false); 2401 mWakeLocks.put(wakeLockKey, wakeLock); 2402 } 2403 return wakeLock; 2404 } 2405 2406 /** 2407 * Defer the specified SyncOperation by rescheduling it on the JobScheduler with some 2408 * delay. 2409 */ 2410 private void deferSyncH(SyncOperation op, long delay) { 2411 mSyncJobService.callJobFinished(op.jobId, false); 2412 if (op.isPeriodic) { 2413 scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay); 2414 } else { 2415 removeSyncOperationFromCache(op.jobId); 2416 scheduleSyncOperationH(op, delay); 2417 } 2418 } 2419 2420 /** 2421 * Cancel an active sync and reschedule it on the JobScheduler with some delay. 2422 */ 2423 private void deferActiveSyncH(ActiveSyncContext asc) { 2424 SyncOperation op = asc.mSyncOperation; 2425 2426 mSyncHandler.obtainMessage(MESSAGE_STOP_SYNC, 0 /* no reschedule */, 2427 0 /* no backoff */, op).sendToTarget(); 2428 deferSyncH(op, SYNC_DELAY_ON_CONFLICT); 2429 } 2430 2431 private void startSyncH(SyncOperation op) { 2432 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2433 if (isLoggable) Slog.v(TAG, op.toString()); 2434 2435 if (mStorageIsLow) { 2436 deferSyncH(op, SYNC_DELAY_ON_LOW_STORAGE); 2437 return; 2438 } 2439 2440 if (op.isPeriodic) { 2441 // Don't allow this periodic to run if a previous instance failed and is currently 2442 // scheduled according to some backoff criteria. 2443 List<SyncOperation> ops = getAllPendingSyncsFromCache(); 2444 for (SyncOperation syncOperation: ops) { 2445 if (syncOperation.sourcePeriodicId == op.jobId) { 2446 mSyncJobService.callJobFinished(op.jobId, false); 2447 return; 2448 } 2449 } 2450 // Don't allow this periodic to run if a previous instance failed and is currently 2451 // executing according to some backoff criteria. 2452 for (ActiveSyncContext asc: mActiveSyncContexts) { 2453 if (asc.mSyncOperation.sourcePeriodicId == op.jobId) { 2454 mSyncJobService.callJobFinished(op.jobId, false); 2455 return; 2456 } 2457 } 2458 // Check for adapter delays. 2459 if (isAdapterDelayed(op.target)) { 2460 deferSyncH(op, 0 /* No minimum delay */); 2461 } 2462 } else { 2463 // Remove SyncOperation entry from mScheduledSyncs cache for non periodic jobs. 2464 removeSyncOperationFromCache(op.jobId); 2465 } 2466 2467 // Check for conflicting syncs. 2468 for (ActiveSyncContext asc: mActiveSyncContexts) { 2469 if (asc.mSyncOperation.isConflict(op)) { 2470 // If the provided SyncOperation conflicts with a running one, the lower 2471 // priority sync is pre-empted. 2472 if (asc.mSyncOperation.findPriority() >= op.findPriority()) { 2473 if (isLoggable) { 2474 Slog.v(TAG, "Rescheduling sync due to conflict " + op.toString()); 2475 } 2476 deferSyncH(op, SYNC_DELAY_ON_CONFLICT); 2477 return; 2478 } else { 2479 if (isLoggable) { 2480 Slog.v(TAG, "Pushing back running sync due to a higher priority sync"); 2481 } 2482 deferActiveSyncH(asc); 2483 } 2484 } 2485 } 2486 2487 if (isOperationValid(op)) { 2488 if (!dispatchSyncOperation(op)) { 2489 mSyncJobService.callJobFinished(op.jobId, false); 2490 } 2491 } else { 2492 mSyncJobService.callJobFinished(op.jobId, false); 2493 } 2494 setAuthorityPendingState(op.target); 2495 } 2496 2497 private ActiveSyncContext findActiveSyncContextH(int jobId) { 2498 for (ActiveSyncContext asc: mActiveSyncContexts) { 2499 SyncOperation op = asc.mSyncOperation; 2500 if (op != null && op.jobId == jobId) { 2501 return asc; 2502 } 2503 } 2504 return null; 2505 } 2506 2507 private void updateRunningAccountsH(EndPoint syncTargets) { 2508 mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts(); 2509 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2510 Slog.v(TAG, "Accounts list: "); 2511 for (AccountAndUser acc : mRunningAccounts) { 2512 Slog.v(TAG, acc.toString()); 2513 } 2514 } 2515 if (mBootCompleted) { 2516 doDatabaseCleanup(); 2517 mSyncStorageEngine.restoreAllPeriodicSyncs(); 2518 } 2519 2520 AccountAndUser[] accounts = mRunningAccounts; 2521 for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) { 2522 if (!containsAccountAndUser(accounts, 2523 currentSyncContext.mSyncOperation.target.account, 2524 currentSyncContext.mSyncOperation.target.userId)) { 2525 Log.d(TAG, "canceling sync since the account is no longer running"); 2526 sendSyncFinishedOrCanceledMessage(currentSyncContext, 2527 null /* no result since this is a cancel */); 2528 } 2529 } 2530 2531 List<SyncOperation> ops = getAllPendingSyncsFromCache(); 2532 for (SyncOperation op: ops) { 2533 if (!containsAccountAndUser(accounts, op.target.account, op.target.userId)) { 2534 removeSyncOperationFromCache(op.jobId); 2535 getJobScheduler().cancel(op.jobId); 2536 } 2537 } 2538 2539 if (syncTargets != null) { 2540 scheduleSync(syncTargets.account, syncTargets.userId, 2541 SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider, null, 0, 0, 2542 true); 2543 } 2544 } 2545 2546 /** 2547 * The given SyncOperation will be removed and a new one scheduled in its place if 2548 * an updated period or flex is specified. 2549 * @param syncOperation SyncOperation whose period and flex is to be updated. 2550 * @param pollFrequencyMillis new period in milliseconds. 2551 * @param flexMillis new flex time in milliseconds. 2552 */ 2553 private void maybeUpdateSyncPeriodH(SyncOperation syncOperation, long pollFrequencyMillis, 2554 long flexMillis) { 2555 if (!(pollFrequencyMillis == syncOperation.periodMillis 2556 && flexMillis == syncOperation.flexMillis)) { 2557 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2558 Slog.v(TAG, "updating period " + syncOperation + " to " + pollFrequencyMillis 2559 + " and flex to " + flexMillis); 2560 } 2561 removePeriodicSyncInternalH(syncOperation); 2562 syncOperation.periodMillis = pollFrequencyMillis; 2563 syncOperation.flexMillis = flexMillis; 2564 scheduleSyncOperationH(syncOperation); 2565 } 2566 } 2567 2568 private void updateOrAddPeriodicSyncH(EndPoint target, long pollFrequency, long flex, 2569 Bundle extras) { 2570 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2571 verifyJobScheduler(); // Will fill in mScheduledSyncs cache if it is not already filled. 2572 final long pollFrequencyMillis = pollFrequency * 1000L; 2573 final long flexMillis = flex * 1000L; 2574 if (isLoggable) { 2575 Slog.v(TAG, "Addition to periodic syncs requested: " + target 2576 + " period: " + pollFrequency 2577 + " flexMillis: " + flex 2578 + " extras: " + extras.toString()); 2579 } 2580 List<SyncOperation> ops = getAllPendingSyncsFromCache(); 2581 for (SyncOperation op: ops) { 2582 if (op.isPeriodic && op.target.matchesSpec(target) 2583 && syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) { 2584 maybeUpdateSyncPeriodH(op, pollFrequencyMillis, flexMillis); 2585 return; 2586 } 2587 } 2588 2589 if (isLoggable) { 2590 Slog.v(TAG, "Adding new periodic sync: " + target 2591 + " period: " + pollFrequency 2592 + " flexMillis: " + flex 2593 + " extras: " + extras.toString()); 2594 } 2595 2596 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> 2597 syncAdapterInfo = mSyncAdapters.getServiceInfo( 2598 SyncAdapterType.newKey( 2599 target.provider, target.account.type), 2600 target.userId); 2601 if (syncAdapterInfo == null) { 2602 return; 2603 } 2604 2605 SyncOperation op = new SyncOperation(target, syncAdapterInfo.uid, 2606 syncAdapterInfo.componentName.getPackageName(), SyncOperation.REASON_PERIODIC, 2607 SyncStorageEngine.SOURCE_PERIODIC, extras, 2608 syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID); 2609 op.periodMillis = pollFrequencyMillis; 2610 op.flexMillis = flexMillis; 2611 scheduleSyncOperationH(op); 2612 mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); 2613 } 2614 2615 /** 2616 * Remove this periodic sync operation and all one-off operations initiated by it. 2617 */ 2618 private void removePeriodicSyncInternalH(SyncOperation syncOperation) { 2619 // Remove this periodic sync and all one-off syncs initiated by it. 2620 List<SyncOperation> ops = getAllPendingSyncsFromCache(); 2621 for (SyncOperation op: ops) { 2622 if (op.sourcePeriodicId == syncOperation.jobId || op.jobId == syncOperation.jobId) { 2623 ActiveSyncContext asc = findActiveSyncContextH(syncOperation.jobId); 2624 if (asc != null) { 2625 mSyncJobService.callJobFinished(syncOperation.jobId, false); 2626 runSyncFinishedOrCanceledH(null, asc); 2627 } 2628 removeSyncOperationFromCache(op.jobId); 2629 getJobScheduler().cancel(op.jobId); 2630 } 2631 } 2632 } 2633 2634 private void removePeriodicSyncH(EndPoint target, Bundle extras) { 2635 verifyJobScheduler(); 2636 List<SyncOperation> ops = getAllPendingSyncsFromCache(); 2637 for (SyncOperation op: ops) { 2638 if (op.isPeriodic && op.target.matchesSpec(target) 2639 && syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) { 2640 removePeriodicSyncInternalH(op); 2641 } 2642 } 2643 } 2644 2645 private boolean isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext) { 2646 final long bytesTransferredCurrent = 2647 getTotalBytesTransferredByUid(activeSyncContext.mSyncAdapterUid); 2648 final long deltaBytesTransferred = 2649 bytesTransferredCurrent - activeSyncContext.mBytesTransferredAtLastPoll; 2650 2651 if (Log.isLoggable(TAG, Log.DEBUG)) { 2652 // Bytes transferred 2653 long remainder = deltaBytesTransferred; 2654 long mb = remainder / (1024 * 1024); 2655 remainder %= 1024 * 1024; 2656 long kb = remainder / 1024; 2657 remainder %= 1024; 2658 long b = remainder; 2659 Log.d(TAG, String.format( 2660 "Time since last update: %ds. Delta transferred: %dMBs,%dKBs,%dBs", 2661 (SystemClock.elapsedRealtime() 2662 - activeSyncContext.mLastPolledTimeElapsed)/1000, 2663 mb, kb, b) 2664 ); 2665 } 2666 return (deltaBytesTransferred <= SYNC_MONITOR_PROGRESS_THRESHOLD_BYTES); 2667 } 2668 2669 /** 2670 * Determine if a sync is no longer valid and should be dropped. 2671 */ 2672 private boolean isOperationValid(SyncOperation op) { 2673 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2674 int state; 2675 final EndPoint target = op.target; 2676 boolean syncEnabled = mSyncStorageEngine.getMasterSyncAutomatically(target.userId); 2677 // Drop the sync if the account of this operation no longer exists. 2678 AccountAndUser[] accounts = mRunningAccounts; 2679 if (!containsAccountAndUser(accounts, target.account, target.userId)) { 2680 if (isLoggable) { 2681 Slog.v(TAG, " Dropping sync operation: account doesn't exist."); 2682 } 2683 return false; 2684 } 2685 // Drop this sync request if it isn't syncable. 2686 state = getIsSyncable(target.account, target.userId, target.provider); 2687 if (state == 0) { 2688 if (isLoggable) { 2689 Slog.v(TAG, " Dropping sync operation: isSyncable == 0."); 2690 } 2691 return false; 2692 } 2693 syncEnabled = syncEnabled && mSyncStorageEngine.getSyncAutomatically( 2694 target.account, target.userId, target.provider); 2695 2696 // We ignore system settings that specify the sync is invalid if: 2697 // 1) It's manual - we try it anyway. When/if it fails it will be rescheduled. 2698 // or 2699 // 2) it's an initialisation sync - we just need to connect to it. 2700 final boolean ignoreSystemConfiguration = op.isIgnoreSettings() || (state < 0); 2701 2702 // Sync not enabled. 2703 if (!syncEnabled && !ignoreSystemConfiguration) { 2704 if (isLoggable) { 2705 Slog.v(TAG, " Dropping sync operation: disallowed by settings/network."); 2706 } 2707 return false; 2708 } 2709 return true; 2710 } 2711 2712 private boolean dispatchSyncOperation(SyncOperation op) { 2713 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2714 Slog.v(TAG, "dispatchSyncOperation: we are going to sync " + op); 2715 Slog.v(TAG, "num active syncs: " + mActiveSyncContexts.size()); 2716 for (ActiveSyncContext syncContext : mActiveSyncContexts) { 2717 Slog.v(TAG, syncContext.toString()); 2718 } 2719 } 2720 // Connect to the sync adapter. 2721 int targetUid; 2722 ComponentName targetComponent; 2723 final SyncStorageEngine.EndPoint info = op.target; 2724 SyncAdapterType syncAdapterType = 2725 SyncAdapterType.newKey(info.provider, info.account.type); 2726 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; 2727 syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, info.userId); 2728 if (syncAdapterInfo == null) { 2729 Log.d(TAG, "can't find a sync adapter for " + syncAdapterType 2730 + ", removing settings for it"); 2731 mSyncStorageEngine.removeAuthority(info); 2732 return false; 2733 } 2734 targetUid = syncAdapterInfo.uid; 2735 targetComponent = syncAdapterInfo.componentName; 2736 ActiveSyncContext activeSyncContext = 2737 new ActiveSyncContext(op, insertStartSyncEvent(op), targetUid); 2738 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2739 Slog.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext); 2740 } 2741 2742 activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext); 2743 mActiveSyncContexts.add(activeSyncContext); 2744 2745 // Post message to begin monitoring this sync's progress. 2746 postMonitorSyncProgressMessage(activeSyncContext); 2747 2748 if (!activeSyncContext.bindToSyncAdapter(targetComponent, info.userId)) { 2749 Slog.e(TAG, "Bind attempt failed - target: " + targetComponent); 2750 closeActiveSyncContext(activeSyncContext); 2751 return false; 2752 } 2753 2754 return true; 2755 } 2756 2757 private void runBoundToAdapterH(final ActiveSyncContext activeSyncContext, 2758 IBinder syncAdapter) { 2759 final SyncOperation syncOperation = activeSyncContext.mSyncOperation; 2760 try { 2761 activeSyncContext.mIsLinkedToDeath = true; 2762 syncAdapter.linkToDeath(activeSyncContext, 0); 2763 2764 activeSyncContext.mSyncAdapter = ISyncAdapter.Stub.asInterface(syncAdapter); 2765 activeSyncContext.mSyncAdapter 2766 .startSync(activeSyncContext, syncOperation.target.provider, 2767 syncOperation.target.account, syncOperation.extras); 2768 } catch (RemoteException remoteExc) { 2769 Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc); 2770 closeActiveSyncContext(activeSyncContext); 2771 increaseBackoffSetting(syncOperation.target); 2772 scheduleSyncOperationH(syncOperation); 2773 } catch (RuntimeException exc) { 2774 closeActiveSyncContext(activeSyncContext); 2775 Slog.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc); 2776 } 2777 } 2778 2779 /** 2780 * Cancel the sync for the provided target that matches the given bundle. 2781 * @param info Can have null fields to indicate all the active syncs for that field. 2782 * @param extras Can be null to indicate <strong>all</strong> syncs for the given endpoint. 2783 */ 2784 private void cancelActiveSyncH(SyncStorageEngine.EndPoint info, Bundle extras) { 2785 ArrayList<ActiveSyncContext> activeSyncs = 2786 new ArrayList<ActiveSyncContext>(mActiveSyncContexts); 2787 for (ActiveSyncContext activeSyncContext : activeSyncs) { 2788 if (activeSyncContext != null) { 2789 final SyncStorageEngine.EndPoint opInfo = 2790 activeSyncContext.mSyncOperation.target; 2791 if (!opInfo.matchesSpec(info)) { 2792 continue; 2793 } 2794 if (extras != null && 2795 !syncExtrasEquals(activeSyncContext.mSyncOperation.extras, 2796 extras, 2797 false /* no config settings */)) { 2798 continue; 2799 } 2800 mSyncJobService.callJobFinished(activeSyncContext.mSyncOperation.jobId, false); 2801 runSyncFinishedOrCanceledH(null /* cancel => no result */, activeSyncContext); 2802 } 2803 } 2804 } 2805 2806 /** 2807 * Should be called when a one-off instance of a periodic sync completes successfully. 2808 */ 2809 private void reschedulePeriodicSyncH(SyncOperation syncOperation) { 2810 removeSyncOperationFromCache(syncOperation.sourcePeriodicId); 2811 getJobScheduler().cancel(syncOperation.sourcePeriodicId); 2812 SyncOperation periodic = syncOperation.createPeriodicSyncOperation(); 2813 scheduleSyncOperationH(periodic); 2814 } 2815 2816 private void runSyncFinishedOrCanceledH(SyncResult syncResult, 2817 ActiveSyncContext activeSyncContext) { 2818 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); 2819 2820 final SyncOperation syncOperation = activeSyncContext.mSyncOperation; 2821 final SyncStorageEngine.EndPoint info = syncOperation.target; 2822 2823 if (activeSyncContext.mIsLinkedToDeath) { 2824 activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0); 2825 activeSyncContext.mIsLinkedToDeath = false; 2826 } 2827 closeActiveSyncContext(activeSyncContext); 2828 final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime; 2829 String historyMessage; 2830 int downstreamActivity; 2831 int upstreamActivity; 2832 if (syncResult != null) { 2833 if (isLoggable) { 2834 Slog.v(TAG, "runSyncFinishedOrCanceled [finished]: " 2835 + syncOperation + ", result " + syncResult); 2836 } 2837 2838 if (!syncResult.hasError()) { 2839 historyMessage = SyncStorageEngine.MESG_SUCCESS; 2840 // TODO: set these correctly when the SyncResult is extended to include it 2841 downstreamActivity = 0; 2842 upstreamActivity = 0; 2843 clearBackoffSetting(syncOperation.target); 2844 2845 // If the operation completes successfully and it was scheduled due to 2846 // a periodic operation failing, we reschedule the periodic operation to 2847 // start from now. 2848 if (syncOperation.isDerivedFromFailedPeriodicSync()) { 2849 reschedulePeriodicSyncH(syncOperation); 2850 } 2851 } else { 2852 Log.d(TAG, "failed sync operation " + syncOperation + ", " + syncResult); 2853 // the operation failed so increase the backoff time 2854 increaseBackoffSetting(syncOperation.target); 2855 if (!syncOperation.isPeriodic) { 2856 // reschedule the sync if so indicated by the syncResult 2857 maybeRescheduleSync(syncResult, syncOperation); 2858 } else { 2859 // create a normal sync instance that will respect adapter backoffs 2860 postScheduleSyncMessage(syncOperation.createOneTimeSyncOperation()); 2861 } 2862 historyMessage = ContentResolver.syncErrorToString( 2863 syncResultToErrorNumber(syncResult)); 2864 // TODO: set these correctly when the SyncResult is extended to include it 2865 downstreamActivity = 0; 2866 upstreamActivity = 0; 2867 } 2868 setDelayUntilTime(syncOperation.target, syncResult.delayUntil); 2869 } else { 2870 if (isLoggable) { 2871 Slog.v(TAG, "runSyncFinishedOrCanceled [canceled]: " + syncOperation); 2872 } 2873 if (activeSyncContext.mSyncAdapter != null) { 2874 try { 2875 activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext); 2876 } catch (RemoteException e) { 2877 // we don't need to retry this in this case 2878 } 2879 } 2880 historyMessage = SyncStorageEngine.MESG_CANCELED; 2881 downstreamActivity = 0; 2882 upstreamActivity = 0; 2883 } 2884 2885 stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage, 2886 upstreamActivity, downstreamActivity, elapsedTime); 2887 // Check for full-resync and schedule it after closing off the last sync. 2888 if (syncResult != null && syncResult.tooManyDeletions) { 2889 installHandleTooManyDeletesNotification(info.account, 2890 info.provider, syncResult.stats.numDeletes, 2891 info.userId); 2892 } else { 2893 mNotificationMgr.cancelAsUser(null, 2894 info.account.hashCode() ^ info.provider.hashCode(), 2895 new UserHandle(info.userId)); 2896 } 2897 if (syncResult != null && syncResult.fullSyncRequested) { 2898 scheduleSyncOperationH( 2899 new SyncOperation(info.account, info.userId, 2900 syncOperation.owningUid, syncOperation.owningPackage, 2901 syncOperation.reason, 2902 syncOperation.syncSource, info.provider, new Bundle(), 2903 syncOperation.allowParallelSyncs)); 2904 } 2905 } 2906 2907 private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) { 2908 activeSyncContext.close(); 2909 mActiveSyncContexts.remove(activeSyncContext); 2910 mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo, 2911 activeSyncContext.mSyncOperation.target.userId); 2912 2913 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2914 Slog.v(TAG, "removing all MESSAGE_MONITOR_SYNC & MESSAGE_SYNC_EXPIRED for " 2915 + activeSyncContext.toString()); 2916 } 2917 mSyncHandler.removeMessages(SyncHandler.MESSAGE_SYNC_EXPIRED, activeSyncContext); 2918 mSyncHandler.removeMessages(SyncHandler.MESSAGE_MONITOR_SYNC, activeSyncContext); 2919 } 2920 2921 /** 2922 * Convert the error-containing SyncResult into the Sync.History error number. Since 2923 * the SyncResult may indicate multiple errors at once, this method just returns the 2924 * most "serious" error. 2925 * @param syncResult the SyncResult from which to read 2926 * @return the most "serious" error set in the SyncResult 2927 * @throws IllegalStateException if the SyncResult does not indicate any errors. 2928 * If SyncResult.error() is true then it is safe to call this. 2929 */ 2930 private int syncResultToErrorNumber(SyncResult syncResult) { 2931 if (syncResult.syncAlreadyInProgress) 2932 return ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS; 2933 if (syncResult.stats.numAuthExceptions > 0) 2934 return ContentResolver.SYNC_ERROR_AUTHENTICATION; 2935 if (syncResult.stats.numIoExceptions > 0) 2936 return ContentResolver.SYNC_ERROR_IO; 2937 if (syncResult.stats.numParseExceptions > 0) 2938 return ContentResolver.SYNC_ERROR_PARSE; 2939 if (syncResult.stats.numConflictDetectedExceptions > 0) 2940 return ContentResolver.SYNC_ERROR_CONFLICT; 2941 if (syncResult.tooManyDeletions) 2942 return ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS; 2943 if (syncResult.tooManyRetries) 2944 return ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES; 2945 if (syncResult.databaseError) 2946 return ContentResolver.SYNC_ERROR_INTERNAL; 2947 throw new IllegalStateException("we are not in an error state, " + syncResult); 2948 } 2949 2950 private void installHandleTooManyDeletesNotification(Account account, String authority, 2951 long numDeletes, int userId) { 2952 if (mNotificationMgr == null) return; 2953 2954 final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider( 2955 authority, 0 /* flags */); 2956 if (providerInfo == null) { 2957 return; 2958 } 2959 CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager()); 2960 2961 Intent clickIntent = new Intent(mContext, SyncActivityTooManyDeletes.class); 2962 clickIntent.putExtra("account", account); 2963 clickIntent.putExtra("authority", authority); 2964 clickIntent.putExtra("provider", authorityName.toString()); 2965 clickIntent.putExtra("numDeletes", numDeletes); 2966 2967 if (!isActivityAvailable(clickIntent)) { 2968 Log.w(TAG, "No activity found to handle too many deletes."); 2969 return; 2970 } 2971 2972 UserHandle user = new UserHandle(userId); 2973 final PendingIntent pendingIntent = PendingIntent 2974 .getActivityAsUser(mContext, 0, clickIntent, 2975 PendingIntent.FLAG_CANCEL_CURRENT, null, user); 2976 2977 CharSequence tooManyDeletesDescFormat = mContext.getResources().getText( 2978 R.string.contentServiceTooManyDeletesNotificationDesc); 2979 2980 Context contextForUser = getContextForUser(user); 2981 Notification notification = new Notification.Builder(contextForUser) 2982 .setSmallIcon(R.drawable.stat_notify_sync_error) 2983 .setTicker(mContext.getString(R.string.contentServiceSync)) 2984 .setWhen(System.currentTimeMillis()) 2985 .setColor(contextForUser.getColor( 2986 com.android.internal.R.color.system_notification_accent_color)) 2987 .setContentTitle(contextForUser.getString( 2988 R.string.contentServiceSyncNotificationTitle)) 2989 .setContentText( 2990 String.format(tooManyDeletesDescFormat.toString(), authorityName)) 2991 .setContentIntent(pendingIntent) 2992 .build(); 2993 notification.flags |= Notification.FLAG_ONGOING_EVENT; 2994 mNotificationMgr.notifyAsUser(null, account.hashCode() ^ authority.hashCode(), 2995 notification, user); 2996 } 2997 2998 /** 2999 * Checks whether an activity exists on the system image for the given intent. 3000 * 3001 * @param intent The intent for an activity. 3002 * @return Whether or not an activity exists. 3003 */ 3004 private boolean isActivityAvailable(Intent intent) { 3005 PackageManager pm = mContext.getPackageManager(); 3006 List<ResolveInfo> list = pm.queryIntentActivities(intent, 0); 3007 int listSize = list.size(); 3008 for (int i = 0; i < listSize; i++) { 3009 ResolveInfo resolveInfo = list.get(i); 3010 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 3011 != 0) { 3012 return true; 3013 } 3014 } 3015 3016 return false; 3017 } 3018 3019 public long insertStartSyncEvent(SyncOperation syncOperation) { 3020 final long now = System.currentTimeMillis(); 3021 EventLog.writeEvent(2720, 3022 syncOperation.toEventLog(SyncStorageEngine.EVENT_START)); 3023 return mSyncStorageEngine.insertStartSyncEvent(syncOperation, now); 3024 } 3025 3026 public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, 3027 int upstreamActivity, int downstreamActivity, long elapsedTime) { 3028 EventLog.writeEvent(2720, 3029 syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP)); 3030 mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime, 3031 resultMessage, downstreamActivity, upstreamActivity); 3032 } 3033 } 3034 3035 private boolean isSyncStillActiveH(ActiveSyncContext activeSyncContext) { 3036 for (ActiveSyncContext sync : mActiveSyncContexts) { 3037 if (sync == activeSyncContext) { 3038 return true; 3039 } 3040 } 3041 return false; 3042 } 3043 3044 /** 3045 * Sync extra comparison function. 3046 * @param b1 bundle to compare 3047 * @param b2 other bundle to compare 3048 * @param includeSyncSettings if false, ignore system settings in bundle. 3049 */ 3050 public static boolean syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSyncSettings) { 3051 if (b1 == b2) { 3052 return true; 3053 } 3054 // Exit early if we can. 3055 if (includeSyncSettings && b1.size() != b2.size()) { 3056 return false; 3057 } 3058 Bundle bigger = b1.size() > b2.size() ? b1 : b2; 3059 Bundle smaller = b1.size() > b2.size() ? b2 : b1; 3060 for (String key : bigger.keySet()) { 3061 if (!includeSyncSettings && isSyncSetting(key)) { 3062 continue; 3063 } 3064 if (!smaller.containsKey(key)) { 3065 return false; 3066 } 3067 if (!Objects.equals(bigger.get(key), smaller.get(key))) { 3068 return false; 3069 } 3070 } 3071 return true; 3072 } 3073 3074 /** 3075 * @return true if the provided key is used by the SyncManager in scheduling the sync. 3076 */ 3077 private static boolean isSyncSetting(String key) { 3078 if (key.equals(ContentResolver.SYNC_EXTRAS_EXPEDITED)) { 3079 return true; 3080 } 3081 if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS)) { 3082 return true; 3083 } 3084 if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF)) { 3085 return true; 3086 } 3087 if (key.equals(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY)) { 3088 return true; 3089 } 3090 if (key.equals(ContentResolver.SYNC_EXTRAS_MANUAL)) { 3091 return true; 3092 } 3093 if (key.equals(ContentResolver.SYNC_EXTRAS_UPLOAD)) { 3094 return true; 3095 } 3096 if (key.equals(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS)) { 3097 return true; 3098 } 3099 if (key.equals(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS)) { 3100 return true; 3101 } 3102 if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD)) { 3103 return true; 3104 } 3105 if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD)) { 3106 return true; 3107 } 3108 if (key.equals(ContentResolver.SYNC_EXTRAS_PRIORITY)) { 3109 return true; 3110 } 3111 if (key.equals(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED)) { 3112 return true; 3113 } 3114 if (key.equals(ContentResolver.SYNC_EXTRAS_INITIALIZE)) { 3115 return true; 3116 } 3117 return false; 3118 } 3119 3120 static class PrintTable { 3121 private ArrayList<Object[]> mTable = Lists.newArrayList(); 3122 private final int mCols; 3123 3124 PrintTable(int cols) { 3125 mCols = cols; 3126 } 3127 3128 void set(int row, int col, Object... values) { 3129 if (col + values.length > mCols) { 3130 throw new IndexOutOfBoundsException("Table only has " + mCols + 3131 " columns. can't set " + values.length + " at column " + col); 3132 } 3133 for (int i = mTable.size(); i <= row; i++) { 3134 final Object[] list = new Object[mCols]; 3135 mTable.add(list); 3136 for (int j = 0; j < mCols; j++) { 3137 list[j] = ""; 3138 } 3139 } 3140 System.arraycopy(values, 0, mTable.get(row), col, values.length); 3141 } 3142 3143 void writeTo(PrintWriter out) { 3144 final String[] formats = new String[mCols]; 3145 int totalLength = 0; 3146 for (int col = 0; col < mCols; ++col) { 3147 int maxLength = 0; 3148 for (Object[] row : mTable) { 3149 final int length = row[col].toString().length(); 3150 if (length > maxLength) { 3151 maxLength = length; 3152 } 3153 } 3154 totalLength += maxLength; 3155 formats[col] = String.format("%%-%ds", maxLength); 3156 } 3157 formats[mCols - 1] = "%s"; 3158 printRow(out, formats, mTable.get(0)); 3159 totalLength += (mCols - 1) * 2; 3160 for (int i = 0; i < totalLength; ++i) { 3161 out.print("-"); 3162 } 3163 out.println(); 3164 for (int i = 1, mTableSize = mTable.size(); i < mTableSize; i++) { 3165 Object[] row = mTable.get(i); 3166 printRow(out, formats, row); 3167 } 3168 } 3169 3170 private void printRow(PrintWriter out, String[] formats, Object[] row) { 3171 for (int j = 0, rowLength = row.length; j < rowLength; j++) { 3172 out.printf(String.format(formats[j], row[j].toString())); 3173 out.print(" "); 3174 } 3175 out.println(); 3176 } 3177 3178 public int getNumRows() { 3179 return mTable.size(); 3180 } 3181 } 3182 3183 private Context getContextForUser(UserHandle user) { 3184 try { 3185 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user); 3186 } catch (NameNotFoundException e) { 3187 // Default to mContext, not finding the package system is running as is unlikely. 3188 return mContext; 3189 } 3190 } 3191} 3192