1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.content; 18 19 import static com.android.server.content.SyncLogger.logSafe; 20 21 import android.accounts.Account; 22 import android.accounts.AccountAndUser; 23 import android.accounts.AccountManager; 24 import android.annotation.Nullable; 25 import android.app.backup.BackupManager; 26 import android.content.ComponentName; 27 import android.content.ContentResolver; 28 import android.content.ContentResolver.SyncExemption; 29 import android.content.Context; 30 import android.content.ISyncStatusObserver; 31 import android.content.PeriodicSync; 32 import android.content.SyncInfo; 33 import android.content.SyncRequest; 34 import android.content.SyncStatusInfo; 35 import android.content.pm.PackageManager; 36 import android.content.pm.PackageManagerInternal; 37 import android.os.Bundle; 38 import android.os.Environment; 39 import android.os.Handler; 40 import android.os.Looper; 41 import android.os.Message; 42 import android.os.Parcel; 43 import android.os.RemoteCallbackList; 44 import android.os.RemoteException; 45 import android.os.UserHandle; 46 import android.util.ArrayMap; 47 import android.util.ArraySet; 48 import android.util.AtomicFile; 49 import android.util.EventLog; 50 import android.util.Log; 51 import android.util.Pair; 52 import android.util.Slog; 53 import android.util.SparseArray; 54 import android.util.Xml; 55 import android.util.proto.ProtoInputStream; 56 import android.util.proto.ProtoOutputStream; 57 58 import com.android.internal.annotations.VisibleForTesting; 59 import com.android.internal.util.ArrayUtils; 60 import com.android.internal.util.IntPair; 61 import com.android.modules.utils.TypedXmlPullParser; 62 import com.android.modules.utils.TypedXmlSerializer; 63 import com.android.server.LocalServices; 64 65 import org.xmlpull.v1.XmlPullParser; 66 import org.xmlpull.v1.XmlPullParserException; 67 68 import java.io.File; 69 import java.io.FileInputStream; 70 import java.io.FileOutputStream; 71 import java.io.IOException; 72 import java.io.InputStream; 73 import java.io.OutputStream; 74 import java.util.ArrayList; 75 import java.util.Calendar; 76 import java.util.HashMap; 77 import java.util.Iterator; 78 import java.util.List; 79 import java.util.Random; 80 import java.util.TimeZone; 81 82 /** 83 * Singleton that tracks the sync data and overall sync 84 * history on the device. 85 * 86 * @hide 87 */ 88 public class SyncStorageEngine { 89 90 private static final String TAG = "SyncManager"; 91 private static final String TAG_FILE = "SyncManagerFile"; 92 93 private static final String XML_ATTR_NEXT_AUTHORITY_ID = "nextAuthorityId"; 94 private static final String XML_ATTR_LISTEN_FOR_TICKLES = "listen-for-tickles"; 95 private static final String XML_ATTR_SYNC_RANDOM_OFFSET = "offsetInSeconds"; 96 private static final String XML_ATTR_ENABLED = "enabled"; 97 private static final String XML_ATTR_USER = "user"; 98 private static final String XML_TAG_LISTEN_FOR_TICKLES = "listenForTickles"; 99 100 /** Default time for a periodic sync. */ 101 private static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day 102 103 /** Percentage of period that is flex by default, if no flexMillis is set. */ 104 private static final double DEFAULT_FLEX_PERCENT_SYNC = 0.04; 105 106 /** Lower bound on sync time from which we assign a default flex time. */ 107 private static final long DEFAULT_MIN_FLEX_ALLOWED_SECS = 5; 108 109 @VisibleForTesting 110 static final long MILLIS_IN_4WEEKS = 1000L * 60 * 60 * 24 * 7 * 4; 111 112 /** Enum value for a sync start event. */ 113 public static final int EVENT_START = 0; 114 115 /** Enum value for a sync stop event. */ 116 public static final int EVENT_STOP = 1; 117 118 /** Enum value for a sync with other sources. */ 119 public static final int SOURCE_OTHER = 0; 120 121 /** Enum value for a local-initiated sync. */ 122 public static final int SOURCE_LOCAL = 1; 123 124 /** Enum value for a poll-based sync (e.g., upon connection to network) */ 125 public static final int SOURCE_POLL = 2; 126 127 /** Enum value for a user-initiated sync. */ 128 public static final int SOURCE_USER = 3; 129 130 /** Enum value for a periodic sync. */ 131 public static final int SOURCE_PERIODIC = 4; 132 133 /** Enum a sync with a "feed" extra */ 134 public static final int SOURCE_FEED = 5; 135 136 public static final long NOT_IN_BACKOFF_MODE = -1; 137 138 /** 139 * String names for the sync source types. 140 * 141 * KEEP THIS AND {@link SyncStatusInfo}.SOURCE_COUNT IN SYNC. 142 */ 143 public static final String[] SOURCES = { 144 "OTHER", 145 "LOCAL", 146 "POLL", 147 "USER", 148 "PERIODIC", 149 "FEED"}; 150 151 // The MESG column will contain one of these or one of the Error types. 152 public static final String MESG_SUCCESS = "success"; 153 public static final String MESG_CANCELED = "canceled"; 154 155 public static final int MAX_HISTORY = 100; 156 157 private static final int MSG_WRITE_STATUS = 1; 158 private static final long WRITE_STATUS_DELAY = 1000*60*10; // 10 minutes 159 160 private static final int MSG_WRITE_STATISTICS = 2; 161 private static final long WRITE_STATISTICS_DELAY = 1000*60*30; // 1/2 hour 162 163 private static final boolean SYNC_ENABLED_DEFAULT = false; 164 165 // the version of the accounts xml file format 166 private static final int ACCOUNTS_VERSION = 3; 167 168 private static HashMap<String, String> sAuthorityRenames; 169 private static PeriodicSyncAddedListener mPeriodicSyncAddedListener; 170 171 private final PackageManagerInternal mPackageManagerInternal; 172 173 private volatile boolean mIsClockValid; 174 175 private volatile boolean mIsJobNamespaceMigrated; 176 private volatile boolean mIsJobAttributionFixed; 177 178 static { 179 sAuthorityRenames = new HashMap<String, String>(); 180 sAuthorityRenames.put("contacts", "com.android.contacts"); 181 sAuthorityRenames.put("calendar", "com.android.calendar"); 182 } 183 184 static class AccountInfo { 185 final AccountAndUser accountAndUser; 186 final HashMap<String, AuthorityInfo> authorities = 187 new HashMap<String, AuthorityInfo>(); 188 AccountInfo(AccountAndUser accountAndUser)189 AccountInfo(AccountAndUser accountAndUser) { 190 this.accountAndUser = accountAndUser; 191 } 192 } 193 194 /** Bare bones representation of a sync target. */ 195 public static class EndPoint { 196 public final static EndPoint USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL = 197 new EndPoint(null, null, UserHandle.USER_ALL); 198 final Account account; 199 final int userId; 200 final String provider; 201 EndPoint(Account account, String provider, int userId)202 public EndPoint(Account account, String provider, int userId) { 203 this.account = account; 204 this.provider = provider; 205 this.userId = userId; 206 } 207 208 /** 209 * An Endpoint for a sync matches if it targets the same sync adapter for the same user. 210 * 211 * @param spec the Endpoint to match. If the spec has null fields, they indicate a wildcard 212 * and match any. 213 */ matchesSpec(EndPoint spec)214 public boolean matchesSpec(EndPoint spec) { 215 if (userId != spec.userId 216 && userId != UserHandle.USER_ALL 217 && spec.userId != UserHandle.USER_ALL) { 218 return false; 219 } 220 boolean accountsMatch; 221 if (spec.account == null) { 222 accountsMatch = true; 223 } else { 224 accountsMatch = account.equals(spec.account); 225 } 226 boolean providersMatch; 227 if (spec.provider == null) { 228 providersMatch = true; 229 } else { 230 providersMatch = provider.equals(spec.provider); 231 } 232 return accountsMatch && providersMatch; 233 } 234 toString()235 public String toString() { 236 StringBuilder sb = new StringBuilder(); 237 sb.append(account == null ? "ALL ACCS" : account.name) 238 .append("/") 239 .append(provider == null ? "ALL PDRS" : provider); 240 sb.append(":u" + userId); 241 return sb.toString(); 242 } 243 toSafeString()244 public String toSafeString() { 245 StringBuilder sb = new StringBuilder(); 246 sb.append(account == null ? "ALL ACCS" : logSafe(account)) 247 .append("/") 248 .append(provider == null ? "ALL PDRS" : provider); 249 sb.append(":u" + userId); 250 return sb.toString(); 251 } 252 } 253 254 public static class AuthorityInfo { 255 // Legal values of getIsSyncable 256 257 /** 258 * The syncable state is undefined. 259 */ 260 public static final int UNDEFINED = -2; 261 262 /** 263 * Default state for a newly installed adapter. An uninitialized adapter will receive an 264 * initialization sync which are governed by a different set of rules to that of regular 265 * syncs. 266 */ 267 public static final int NOT_INITIALIZED = -1; 268 /** 269 * The adapter will not receive any syncs. This is behaviourally equivalent to 270 * setSyncAutomatically -> false. However setSyncAutomatically is surfaced to the user 271 * while this is generally meant to be controlled by the developer. 272 */ 273 public static final int NOT_SYNCABLE = 0; 274 /** 275 * The adapter is initialized and functioning. This is the normal state for an adapter. 276 */ 277 public static final int SYNCABLE = 1; 278 /** 279 * The adapter is syncable but still requires an initialization sync. For example an adapter 280 * than has been restored from a previous device will be in this state. Not meant for 281 * external use. 282 */ 283 public static final int SYNCABLE_NOT_INITIALIZED = 2; 284 285 /** 286 * The adapter is syncable but does not have access to the synced account and needs a 287 * user access approval. 288 */ 289 public static final int SYNCABLE_NO_ACCOUNT_ACCESS = 3; 290 291 final EndPoint target; 292 final int ident; 293 boolean enabled; 294 int syncable; 295 /** Time at which this sync will run, taking into account backoff. */ 296 long backoffTime; 297 /** Amount of delay due to backoff. */ 298 long backoffDelay; 299 /** Time offset to add to any requests coming to this target. */ 300 long delayUntil; 301 302 final ArrayList<PeriodicSync> periodicSyncs; 303 304 /** 305 * Copy constructor for making deep-ish copies. Only the bundles stored 306 * in periodic syncs can make unexpected changes. 307 * 308 * @param toCopy AuthorityInfo to be copied. 309 */ AuthorityInfo(AuthorityInfo toCopy)310 AuthorityInfo(AuthorityInfo toCopy) { 311 target = toCopy.target; 312 ident = toCopy.ident; 313 enabled = toCopy.enabled; 314 syncable = toCopy.syncable; 315 backoffTime = toCopy.backoffTime; 316 backoffDelay = toCopy.backoffDelay; 317 delayUntil = toCopy.delayUntil; 318 periodicSyncs = new ArrayList<PeriodicSync>(); 319 for (PeriodicSync sync : toCopy.periodicSyncs) { 320 // Still not a perfect copy, because we are just copying the mappings. 321 periodicSyncs.add(new PeriodicSync(sync)); 322 } 323 } 324 AuthorityInfo(EndPoint info, int id)325 AuthorityInfo(EndPoint info, int id) { 326 target = info; 327 ident = id; 328 enabled = SYNC_ENABLED_DEFAULT; 329 periodicSyncs = new ArrayList<PeriodicSync>(); 330 defaultInitialisation(); 331 } 332 defaultInitialisation()333 private void defaultInitialisation() { 334 syncable = NOT_INITIALIZED; // default to "unknown" 335 backoffTime = -1; // if < 0 then we aren't in backoff mode 336 backoffDelay = -1; // if < 0 then we aren't in backoff mode 337 338 if (mPeriodicSyncAddedListener != null) { 339 mPeriodicSyncAddedListener.onPeriodicSyncAdded(target, new Bundle(), 340 DEFAULT_POLL_FREQUENCY_SECONDS, 341 calculateDefaultFlexTime(DEFAULT_POLL_FREQUENCY_SECONDS)); 342 } 343 } 344 345 @Override toString()346 public String toString() { 347 return target + ", enabled=" + enabled + ", syncable=" + syncable + ", backoff=" 348 + backoffTime + ", delay=" + delayUntil; 349 } 350 toSafeString()351 public String toSafeString() { 352 return target.toSafeString() + ", enabled=" + enabled + ", syncable=" + syncable 353 + ", backoff=" + backoffTime + ", delay=" + delayUntil; 354 } 355 } 356 357 public static class SyncHistoryItem { 358 int authorityId; 359 int historyId; 360 long eventTime; 361 long elapsedTime; 362 int source; 363 int event; 364 long upstreamActivity; 365 long downstreamActivity; 366 String mesg; 367 boolean initialization; 368 Bundle extras; 369 int reason; 370 int syncExemptionFlag; 371 } 372 373 public static class DayStats { 374 public final int day; 375 public int successCount; 376 public long successTime; 377 public int failureCount; 378 public long failureTime; 379 DayStats(int day)380 public DayStats(int day) { 381 this.day = day; 382 } 383 } 384 385 interface OnSyncRequestListener { 386 387 /** Called when a sync is needed on an account(s) due to some change in state. */ onSyncRequest(EndPoint info, int reason, Bundle extras, @SyncExemption int syncExemptionFlag, int callingUid, int callingPid)388 public void onSyncRequest(EndPoint info, int reason, Bundle extras, 389 @SyncExemption int syncExemptionFlag, int callingUid, int callingPid); 390 } 391 392 interface PeriodicSyncAddedListener { 393 /** Called when a periodic sync is added. */ onPeriodicSyncAdded(EndPoint target, Bundle extras, long pollFrequency, long flex)394 void onPeriodicSyncAdded(EndPoint target, Bundle extras, long pollFrequency, long flex); 395 } 396 397 interface OnAuthorityRemovedListener { 398 /** Called when an authority is removed. */ onAuthorityRemoved(EndPoint removedAuthority)399 void onAuthorityRemoved(EndPoint removedAuthority); 400 } 401 402 /** 403 * Validator that maintains a lazy cache of accounts and providers to tell if an authority or 404 * account is valid. 405 */ 406 private static class AccountAuthorityValidator { 407 final private AccountManager mAccountManager; 408 final private PackageManager mPackageManager; 409 final private SparseArray<Account[]> mAccountsCache; 410 final private SparseArray<ArrayMap<String, Boolean>> mProvidersPerUserCache; 411 AccountAuthorityValidator(Context context)412 AccountAuthorityValidator(Context context) { 413 mAccountManager = context.getSystemService(AccountManager.class); 414 mPackageManager = context.getPackageManager(); 415 mAccountsCache = new SparseArray<>(); 416 mProvidersPerUserCache = new SparseArray<>(); 417 } 418 419 // An account is valid if an installed authenticator has previously created that account 420 // on the device isAccountValid(Account account, int userId)421 boolean isAccountValid(Account account, int userId) { 422 Account[] accountsForUser = mAccountsCache.get(userId); 423 if (accountsForUser == null) { 424 accountsForUser = mAccountManager.getAccountsAsUser(userId); 425 mAccountsCache.put(userId, accountsForUser); 426 } 427 return ArrayUtils.contains(accountsForUser, account); 428 } 429 430 // An authority is only valid if it has a content provider installed on the system isAuthorityValid(String authority, int userId)431 boolean isAuthorityValid(String authority, int userId) { 432 ArrayMap<String, Boolean> authorityMap = mProvidersPerUserCache.get(userId); 433 if (authorityMap == null) { 434 authorityMap = new ArrayMap<>(); 435 mProvidersPerUserCache.put(userId, authorityMap); 436 } 437 if (!authorityMap.containsKey(authority)) { 438 authorityMap.put(authority, mPackageManager.resolveContentProviderAsUser(authority, 439 PackageManager.MATCH_DIRECT_BOOT_AWARE 440 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId) != null); 441 } 442 return authorityMap.get(authority); 443 } 444 } 445 446 // Primary list of all syncable authorities. Also our global lock. 447 @VisibleForTesting 448 final SparseArray<AuthorityInfo> mAuthorities = 449 new SparseArray<AuthorityInfo>(); 450 451 private final HashMap<AccountAndUser, AccountInfo> mAccounts 452 = new HashMap<AccountAndUser, AccountInfo>(); 453 454 private final SparseArray<ArrayList<SyncInfo>> mCurrentSyncs 455 = new SparseArray<ArrayList<SyncInfo>>(); 456 457 @VisibleForTesting 458 final SparseArray<SyncStatusInfo> mSyncStatus = 459 new SparseArray<SyncStatusInfo>(); 460 461 private final ArrayList<SyncHistoryItem> mSyncHistory = 462 new ArrayList<SyncHistoryItem>(); 463 464 private final RemoteCallbackList<ISyncStatusObserver> mChangeListeners 465 = new RemoteCallbackList<ISyncStatusObserver>(); 466 467 /** Reverse mapping for component name -> <userid -> target id>. */ 468 private final ArrayMap<ComponentName, SparseArray<AuthorityInfo>> mServices = 469 new ArrayMap<ComponentName, SparseArray<AuthorityInfo>>(); 470 471 private int mNextAuthorityId = 0; 472 473 // We keep 4 weeks of stats. 474 @VisibleForTesting 475 final DayStats[] mDayStats = new DayStats[7*4]; 476 private final Calendar mCal; 477 private int mYear; 478 private int mYearInDays; 479 480 private final Context mContext; 481 482 private static volatile SyncStorageEngine sSyncStorageEngine = null; 483 484 private int mSyncRandomOffset; 485 486 private static final boolean DELETE_LEGACY_PARCEL_FILES = true; 487 private static final String LEGACY_STATUS_FILE_NAME = "status.bin"; 488 private static final String LEGACY_STATISTICS_FILE_NAME = "stats.bin"; 489 490 private static final String SYNC_DIR_NAME = "sync"; 491 private static final String ACCOUNT_INFO_FILE_NAME = "accounts.xml"; 492 private static final String STATUS_FILE_NAME = "status"; 493 private static final String STATISTICS_FILE_NAME = "stats"; 494 495 private File mSyncDir; 496 497 /** 498 * This file contains the core engine state: all accounts and the 499 * settings for them. It must never be lost, and should be changed 500 * infrequently, so it is stored as an XML file. 501 */ 502 private final AtomicFile mAccountInfoFile; 503 504 /** 505 * This file contains the current sync status. We would like to retain 506 * it across boots, but its loss is not the end of the world, so we store 507 * this information as binary data. 508 */ 509 private final AtomicFile mStatusFile; 510 511 /** 512 * This file contains sync statistics. This is purely debugging information 513 * so is written infrequently and can be thrown away at any time. 514 */ 515 private final AtomicFile mStatisticsFile; 516 517 private int mNextHistoryId = 0; 518 private SparseArray<Boolean> mMasterSyncAutomatically = new SparseArray<Boolean>(); 519 private boolean mDefaultMasterSyncAutomatically; 520 521 private OnSyncRequestListener mSyncRequestListener; 522 private OnAuthorityRemovedListener mAuthorityRemovedListener; 523 524 private boolean mGrantSyncAdaptersAccountAccess; 525 526 private final MyHandler mHandler; 527 private final SyncLogger mLogger; 528 SyncStorageEngine(Context context, File dataDir, Looper looper)529 private SyncStorageEngine(Context context, File dataDir, Looper looper) { 530 mHandler = new MyHandler(looper); 531 mContext = context; 532 sSyncStorageEngine = this; 533 mLogger = SyncLogger.getInstance(); 534 535 mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0")); 536 537 mDefaultMasterSyncAutomatically = mContext.getResources().getBoolean( 538 com.android.internal.R.bool.config_syncstorageengine_masterSyncAutomatically); 539 540 mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); 541 542 File systemDir = new File(dataDir, "system"); 543 mSyncDir = new File(systemDir, SYNC_DIR_NAME); 544 mSyncDir.mkdirs(); 545 546 maybeDeleteLegacyPendingInfoLocked(mSyncDir); 547 548 mAccountInfoFile = new AtomicFile(new File(mSyncDir, ACCOUNT_INFO_FILE_NAME), 549 "sync-accounts"); 550 mStatusFile = new AtomicFile(new File(mSyncDir, STATUS_FILE_NAME), "sync-status"); 551 mStatisticsFile = new AtomicFile(new File(mSyncDir, STATISTICS_FILE_NAME), "sync-stats"); 552 553 readAccountInfoLocked(); 554 readStatusLocked(); 555 readStatisticsLocked(); 556 557 if (mLogger.enabled()) { 558 final int size = mAuthorities.size(); 559 mLogger.log("Loaded ", size, " items"); 560 for (int i = 0; i < size; i++) { 561 mLogger.log(mAuthorities.valueAt(i).toSafeString()); 562 } 563 } 564 } 565 newTestInstance(Context context)566 public static SyncStorageEngine newTestInstance(Context context) { 567 return new SyncStorageEngine(context, context.getFilesDir(), Looper.getMainLooper()); 568 } 569 init(Context context, Looper looper)570 public static void init(Context context, Looper looper) { 571 if (sSyncStorageEngine != null) { 572 return; 573 } 574 File dataDir = Environment.getDataDirectory(); 575 sSyncStorageEngine = new SyncStorageEngine(context, dataDir, looper); 576 } 577 getSingleton()578 public static SyncStorageEngine getSingleton() { 579 if (sSyncStorageEngine == null) { 580 throw new IllegalStateException("not initialized"); 581 } 582 return sSyncStorageEngine; 583 } 584 setOnSyncRequestListener(OnSyncRequestListener listener)585 protected void setOnSyncRequestListener(OnSyncRequestListener listener) { 586 if (mSyncRequestListener == null) { 587 mSyncRequestListener = listener; 588 } 589 } 590 setOnAuthorityRemovedListener(OnAuthorityRemovedListener listener)591 protected void setOnAuthorityRemovedListener(OnAuthorityRemovedListener listener) { 592 if (mAuthorityRemovedListener == null) { 593 mAuthorityRemovedListener = listener; 594 } 595 } 596 setPeriodicSyncAddedListener(PeriodicSyncAddedListener listener)597 protected void setPeriodicSyncAddedListener(PeriodicSyncAddedListener listener) { 598 if (mPeriodicSyncAddedListener == null) { 599 mPeriodicSyncAddedListener = listener; 600 } 601 } 602 603 private class MyHandler extends Handler { MyHandler(Looper looper)604 public MyHandler(Looper looper) { 605 super(looper); 606 } 607 608 @Override handleMessage(Message msg)609 public void handleMessage(Message msg) { 610 if (msg.what == MSG_WRITE_STATUS) { 611 synchronized (mAuthorities) { 612 writeStatusLocked(); 613 } 614 } else if (msg.what == MSG_WRITE_STATISTICS) { 615 synchronized (mAuthorities) { 616 writeStatisticsLocked(); 617 } 618 } 619 } 620 } 621 getSyncRandomOffset()622 public int getSyncRandomOffset() { 623 return mSyncRandomOffset; 624 } 625 addStatusChangeListener(int mask, int callingUid, ISyncStatusObserver callback)626 public void addStatusChangeListener(int mask, int callingUid, ISyncStatusObserver callback) { 627 synchronized (mAuthorities) { 628 final long cookie = IntPair.of(callingUid, mask); 629 mChangeListeners.register(callback, cookie); 630 } 631 } 632 removeStatusChangeListener(ISyncStatusObserver callback)633 public void removeStatusChangeListener(ISyncStatusObserver callback) { 634 synchronized (mAuthorities) { 635 mChangeListeners.unregister(callback); 636 } 637 } 638 639 /** 640 * Figure out a reasonable flex time for cases where none is provided (old api calls). 641 * @param syncTimeSeconds requested sync time from now. 642 * @return amount of seconds before syncTimeSeconds that the sync can occur. 643 * I.e. 644 * earliest_sync_time = syncTimeSeconds - calculateDefaultFlexTime(syncTimeSeconds) 645 * The flex time is capped at a percentage of the {@link #DEFAULT_POLL_FREQUENCY_SECONDS}. 646 */ calculateDefaultFlexTime(long syncTimeSeconds)647 public static long calculateDefaultFlexTime(long syncTimeSeconds) { 648 if (syncTimeSeconds < DEFAULT_MIN_FLEX_ALLOWED_SECS) { 649 // Small enough sync request time that we don't add flex time - developer probably 650 // wants to wait for an operation to occur before syncing so we honour the 651 // request time. 652 return 0L; 653 } else if (syncTimeSeconds < DEFAULT_POLL_FREQUENCY_SECONDS) { 654 return (long) (syncTimeSeconds * DEFAULT_FLEX_PERCENT_SYNC); 655 } else { 656 // Large enough sync request time that we cap the flex time. 657 return (long) (DEFAULT_POLL_FREQUENCY_SECONDS * DEFAULT_FLEX_PERCENT_SYNC); 658 } 659 } 660 reportChange(int which, EndPoint target)661 void reportChange(int which, EndPoint target) { 662 final String syncAdapterPackageName; 663 if (target.account == null || target.provider == null) { 664 syncAdapterPackageName = null; 665 } else { 666 syncAdapterPackageName = ContentResolver.getSyncAdapterPackageAsUser( 667 target.account.type, target.provider, target.userId); 668 } 669 reportChange(which, syncAdapterPackageName, target.userId); 670 } 671 reportChange(int which, String callingPackageName, int callingUserId)672 void reportChange(int which, String callingPackageName, int callingUserId) { 673 ArrayList<ISyncStatusObserver> reports = null; 674 synchronized (mAuthorities) { 675 int i = mChangeListeners.beginBroadcast(); 676 while (i > 0) { 677 i--; 678 final long cookie = (long) mChangeListeners.getBroadcastCookie(i); 679 final int registerUid = IntPair.first(cookie); 680 final int registerUserId = UserHandle.getUserId(registerUid); 681 final int mask = IntPair.second(cookie); 682 if ((which & mask) == 0 || callingUserId != registerUserId) { 683 continue; 684 } 685 if (callingPackageName != null && mPackageManagerInternal.filterAppAccess( 686 callingPackageName, registerUid, callingUserId)) { 687 continue; 688 } 689 if (reports == null) { 690 reports = new ArrayList<ISyncStatusObserver>(i); 691 } 692 reports.add(mChangeListeners.getBroadcastItem(i)); 693 } 694 mChangeListeners.finishBroadcast(); 695 } 696 697 if (Log.isLoggable(TAG, Log.VERBOSE)) { 698 Slog.v(TAG, "reportChange " + which + " to: " + reports); 699 } 700 701 if (reports != null) { 702 int i = reports.size(); 703 while (i > 0) { 704 i--; 705 try { 706 reports.get(i).onStatusChanged(which); 707 } catch (RemoteException e) { 708 // The remote callback list will take care of this for us. 709 } 710 } 711 } 712 } 713 getSyncAutomatically(Account account, int userId, String providerName)714 public boolean getSyncAutomatically(Account account, int userId, String providerName) { 715 synchronized (mAuthorities) { 716 if (account != null) { 717 AuthorityInfo authority = getAuthorityLocked( 718 new EndPoint(account, providerName, userId), 719 "getSyncAutomatically"); 720 return authority != null && authority.enabled; 721 } 722 723 int i = mAuthorities.size(); 724 while (i > 0) { 725 i--; 726 AuthorityInfo authorityInfo = mAuthorities.valueAt(i); 727 if (authorityInfo.target.matchesSpec(new EndPoint(account, providerName, userId)) 728 && authorityInfo.enabled) { 729 return true; 730 } 731 } 732 return false; 733 } 734 } 735 setSyncAutomatically(Account account, int userId, String providerName, boolean sync, @SyncExemption int syncExemptionFlag, int callingUid, int callingPid)736 public void setSyncAutomatically(Account account, int userId, String providerName, 737 boolean sync, @SyncExemption int syncExemptionFlag, int callingUid, int callingPid) { 738 if (Log.isLoggable(TAG, Log.VERBOSE)) { 739 Slog.d(TAG, "setSyncAutomatically: " + /* account + */" provider " + providerName 740 + ", user " + userId + " -> " + sync); 741 } 742 mLogger.log("Set sync auto account=", account.toSafeString(), 743 " user=", userId, 744 " authority=", providerName, 745 " value=", Boolean.toString(sync), 746 " cuid=", callingUid, 747 " cpid=", callingPid 748 ); 749 final AuthorityInfo authority; 750 synchronized (mAuthorities) { 751 authority = getOrCreateAuthorityLocked( 752 new EndPoint(account, providerName, userId), 753 -1 /* ident */, 754 false); 755 if (authority.enabled == sync) { 756 if (Log.isLoggable(TAG, Log.VERBOSE)) { 757 Slog.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing"); 758 } 759 return; 760 } 761 // If the adapter was syncable but missing its initialization sync, set it to 762 // uninitialized now. This is to give it a chance to run any one-time initialization 763 // logic. 764 if (sync && authority.syncable == AuthorityInfo.SYNCABLE_NOT_INITIALIZED) { 765 authority.syncable = AuthorityInfo.NOT_INITIALIZED; 766 } 767 authority.enabled = sync; 768 writeAccountInfoLocked(); 769 } 770 771 if (sync) { 772 requestSync(account, userId, SyncOperation.REASON_SYNC_AUTO, providerName, 773 new Bundle(), 774 syncExemptionFlag, callingUid, callingPid); 775 } 776 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, authority.target); 777 queueBackup(); 778 } 779 getIsSyncable(Account account, int userId, String providerName)780 public int getIsSyncable(Account account, int userId, String providerName) { 781 synchronized (mAuthorities) { 782 if (account != null) { 783 AuthorityInfo authority = getAuthorityLocked( 784 new EndPoint(account, providerName, userId), 785 "get authority syncable"); 786 if (authority == null) { 787 return AuthorityInfo.NOT_INITIALIZED; 788 } 789 return authority.syncable; 790 } 791 792 int i = mAuthorities.size(); 793 while (i > 0) { 794 i--; 795 AuthorityInfo authorityInfo = mAuthorities.valueAt(i); 796 if (authorityInfo.target != null 797 && authorityInfo.target.provider.equals(providerName)) { 798 return authorityInfo.syncable; 799 } 800 } 801 return AuthorityInfo.NOT_INITIALIZED; 802 } 803 } 804 setIsSyncable(Account account, int userId, String providerName, int syncable, int callingUid, int callingPid)805 public void setIsSyncable(Account account, int userId, String providerName, int syncable, 806 int callingUid, int callingPid) { 807 setSyncableStateForEndPoint(new EndPoint(account, providerName, userId), syncable, 808 callingUid, callingPid); 809 } 810 811 /** 812 * An enabled sync service and a syncable provider's adapter both get resolved to the same 813 * persisted variable - namely the "syncable" attribute for an AuthorityInfo in accounts.xml. 814 * @param target target to set value for. 815 * @param syncable 0 indicates unsyncable, <0 unknown, >0 is active/syncable. 816 */ setSyncableStateForEndPoint(EndPoint target, int syncable, int callingUid, int callingPid)817 private void setSyncableStateForEndPoint(EndPoint target, int syncable, 818 int callingUid, int callingPid) { 819 AuthorityInfo aInfo; 820 mLogger.log("Set syncable ", target.toSafeString(), 821 " value=", Integer.toString(syncable), 822 " cuid=", callingUid, 823 " cpid=", callingPid); 824 synchronized (mAuthorities) { 825 aInfo = getOrCreateAuthorityLocked(target, -1, false); 826 if (syncable < AuthorityInfo.NOT_INITIALIZED) { 827 syncable = AuthorityInfo.NOT_INITIALIZED; 828 } 829 if (Log.isLoggable(TAG, Log.VERBOSE)) { 830 Slog.d(TAG, "setIsSyncable: " + aInfo.toString() + " -> " + syncable); 831 } 832 if (aInfo.syncable == syncable) { 833 if (Log.isLoggable(TAG, Log.VERBOSE)) { 834 Slog.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing"); 835 } 836 return; 837 } 838 aInfo.syncable = syncable; 839 writeAccountInfoLocked(); 840 } 841 if (syncable == AuthorityInfo.SYNCABLE) { 842 requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle(), 843 ContentResolver.SYNC_EXEMPTION_NONE, callingUid, callingPid); 844 } 845 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, target); 846 } 847 setJobNamespaceMigrated(boolean migrated)848 void setJobNamespaceMigrated(boolean migrated) { 849 if (mIsJobNamespaceMigrated == migrated) { 850 return; 851 } 852 mIsJobNamespaceMigrated = migrated; 853 // This isn't urgent enough to write synchronously. Post it to the handler thread so 854 // SyncManager can move on with whatever it was doing. 855 mHandler.sendEmptyMessageDelayed(MSG_WRITE_STATUS, WRITE_STATUS_DELAY); 856 } 857 isJobNamespaceMigrated()858 boolean isJobNamespaceMigrated() { 859 return mIsJobNamespaceMigrated; 860 } 861 setJobAttributionFixed(boolean fixed)862 void setJobAttributionFixed(boolean fixed) { 863 if (mIsJobAttributionFixed == fixed) { 864 return; 865 } 866 mIsJobAttributionFixed = fixed; 867 // This isn't urgent enough to write synchronously. Post it to the handler thread so 868 // SyncManager can move on with whatever it was doing. 869 mHandler.sendEmptyMessageDelayed(MSG_WRITE_STATUS, WRITE_STATUS_DELAY); 870 } 871 isJobAttributionFixed()872 boolean isJobAttributionFixed() { 873 return mIsJobAttributionFixed; 874 } 875 getBackoff(EndPoint info)876 public Pair<Long, Long> getBackoff(EndPoint info) { 877 synchronized (mAuthorities) { 878 AuthorityInfo authority = getAuthorityLocked(info, "getBackoff"); 879 if (authority != null) { 880 return Pair.create(authority.backoffTime, authority.backoffDelay); 881 } 882 return null; 883 } 884 } 885 886 /** 887 * Update the backoff for the given endpoint. The endpoint may be for a provider/account and 888 * the account or provider info be null, which signifies all accounts or providers. 889 */ setBackoff(EndPoint info, long nextSyncTime, long nextDelay)890 public void setBackoff(EndPoint info, long nextSyncTime, long nextDelay) { 891 if (Log.isLoggable(TAG, Log.VERBOSE)) { 892 Slog.v(TAG, "setBackoff: " + info 893 + " -> nextSyncTime " + nextSyncTime + ", nextDelay " + nextDelay); 894 } 895 boolean changed; 896 synchronized (mAuthorities) { 897 if (info.account == null || info.provider == null) { 898 // Do more work for a provider sync if the provided info has specified all 899 // accounts/providers. 900 changed = setBackoffLocked( 901 info.account /* may be null */, 902 info.userId, 903 info.provider /* may be null */, 904 nextSyncTime, nextDelay); 905 } else { 906 AuthorityInfo authorityInfo = 907 getOrCreateAuthorityLocked(info, -1 /* ident */, true); 908 if (authorityInfo.backoffTime == nextSyncTime 909 && authorityInfo.backoffDelay == nextDelay) { 910 changed = false; 911 } else { 912 authorityInfo.backoffTime = nextSyncTime; 913 authorityInfo.backoffDelay = nextDelay; 914 changed = true; 915 } 916 } 917 } 918 if (changed) { 919 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info); 920 } 921 } 922 923 /** 924 * Either set backoff for a specific authority, or set backoff for all the 925 * accounts on a specific adapter/all adapters. 926 * 927 * @param account account for which to set backoff. Null to specify all accounts. 928 * @param userId id of the user making this request. 929 * @param providerName provider for which to set backoff. Null to specify all providers. 930 * @return true if a change occured. 931 */ setBackoffLocked(Account account, int userId, String providerName, long nextSyncTime, long nextDelay)932 private boolean setBackoffLocked(Account account, int userId, String providerName, 933 long nextSyncTime, long nextDelay) { 934 boolean changed = false; 935 for (AccountInfo accountInfo : mAccounts.values()) { 936 if (account != null && !account.equals(accountInfo.accountAndUser.account) 937 && userId != accountInfo.accountAndUser.userId) { 938 continue; 939 } 940 for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) { 941 if (providerName != null 942 && !providerName.equals(authorityInfo.target.provider)) { 943 continue; 944 } 945 if (authorityInfo.backoffTime != nextSyncTime 946 || authorityInfo.backoffDelay != nextDelay) { 947 authorityInfo.backoffTime = nextSyncTime; 948 authorityInfo.backoffDelay = nextDelay; 949 changed = true; 950 } 951 } 952 } 953 return changed; 954 } 955 clearAllBackoffsLocked()956 public void clearAllBackoffsLocked() { 957 final ArraySet<Integer> changedUserIds = new ArraySet<>(); 958 synchronized (mAuthorities) { 959 // Clear backoff for all sync adapters. 960 for (AccountInfo accountInfo : mAccounts.values()) { 961 for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) { 962 if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE 963 || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) { 964 if (Log.isLoggable(TAG, Log.VERBOSE)) { 965 Slog.v(TAG, "clearAllBackoffsLocked:" 966 + " authority:" + authorityInfo.target 967 + " account:" + accountInfo.accountAndUser.account.name 968 + " user:" + accountInfo.accountAndUser.userId 969 + " backoffTime was: " + authorityInfo.backoffTime 970 + " backoffDelay was: " + authorityInfo.backoffDelay); 971 } 972 authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE; 973 authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE; 974 changedUserIds.add(accountInfo.accountAndUser.userId); 975 } 976 } 977 } 978 } 979 980 for (int i = changedUserIds.size() - 1; i > 0; i--) { 981 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, 982 null /* callingPackageName */, changedUserIds.valueAt(i)); 983 } 984 } 985 getDelayUntilTime(EndPoint info)986 public long getDelayUntilTime(EndPoint info) { 987 synchronized (mAuthorities) { 988 AuthorityInfo authority = getAuthorityLocked(info, "getDelayUntil"); 989 if (authority == null) { 990 return 0; 991 } 992 return authority.delayUntil; 993 } 994 } 995 setDelayUntilTime(EndPoint info, long delayUntil)996 public void setDelayUntilTime(EndPoint info, long delayUntil) { 997 if (Log.isLoggable(TAG, Log.VERBOSE)) { 998 Slog.v(TAG, "setDelayUntil: " + info 999 + " -> delayUntil " + delayUntil); 1000 } 1001 synchronized (mAuthorities) { 1002 AuthorityInfo authority = getOrCreateAuthorityLocked(info, -1, true); 1003 if (authority.delayUntil == delayUntil) { 1004 return; 1005 } 1006 authority.delayUntil = delayUntil; 1007 } 1008 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info); 1009 } 1010 1011 /** 1012 * Restore all periodic syncs read from persisted files. Used to restore periodic syncs 1013 * after an OS update. 1014 */ restoreAllPeriodicSyncs()1015 boolean restoreAllPeriodicSyncs() { 1016 if (mPeriodicSyncAddedListener == null) { 1017 return false; 1018 } 1019 synchronized (mAuthorities) { 1020 for (int i=0; i<mAuthorities.size(); i++) { 1021 AuthorityInfo authority = mAuthorities.valueAt(i); 1022 for (PeriodicSync periodicSync: authority.periodicSyncs) { 1023 mPeriodicSyncAddedListener.onPeriodicSyncAdded(authority.target, 1024 periodicSync.extras, periodicSync.period, periodicSync.flexTime); 1025 } 1026 authority.periodicSyncs.clear(); 1027 } 1028 writeAccountInfoLocked(); 1029 } 1030 return true; 1031 } 1032 setMasterSyncAutomatically(boolean flag, int userId, @SyncExemption int syncExemptionFlag, int callingUid, int callingPid)1033 public void setMasterSyncAutomatically(boolean flag, int userId, 1034 @SyncExemption int syncExemptionFlag, int callingUid, int callingPid) { 1035 mLogger.log("Set master enabled=", flag, " user=", userId, 1036 " cuid=", callingUid, 1037 " cpid=", callingPid); 1038 synchronized (mAuthorities) { 1039 Boolean auto = mMasterSyncAutomatically.get(userId); 1040 if (auto != null && auto.equals(flag)) { 1041 return; 1042 } 1043 mMasterSyncAutomatically.put(userId, flag); 1044 writeAccountInfoLocked(); 1045 } 1046 if (flag) { 1047 requestSync(null, userId, SyncOperation.REASON_MASTER_SYNC_AUTO, null, 1048 new Bundle(), 1049 syncExemptionFlag, callingUid, callingPid); 1050 } 1051 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, 1052 null /* callingPackageName */, userId); 1053 mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED); 1054 queueBackup(); 1055 } 1056 getMasterSyncAutomatically(int userId)1057 public boolean getMasterSyncAutomatically(int userId) { 1058 synchronized (mAuthorities) { 1059 Boolean auto = mMasterSyncAutomatically.get(userId); 1060 return auto == null ? mDefaultMasterSyncAutomatically : auto; 1061 } 1062 } 1063 getAuthorityCount()1064 public int getAuthorityCount() { 1065 synchronized (mAuthorities) { 1066 return mAuthorities.size(); 1067 } 1068 } 1069 getAuthority(int authorityId)1070 public AuthorityInfo getAuthority(int authorityId) { 1071 synchronized (mAuthorities) { 1072 return mAuthorities.get(authorityId); 1073 } 1074 } 1075 1076 /** 1077 * Returns true if there is currently a sync operation being actively processed for the given 1078 * target. 1079 */ isSyncActive(EndPoint info)1080 public boolean isSyncActive(EndPoint info) { 1081 synchronized (mAuthorities) { 1082 for (SyncInfo syncInfo : getCurrentSyncs(info.userId)) { 1083 AuthorityInfo ainfo = getAuthority(syncInfo.authorityId); 1084 if (ainfo != null && ainfo.target.matchesSpec(info)) { 1085 return true; 1086 } 1087 } 1088 } 1089 return false; 1090 } 1091 markPending(EndPoint info, boolean pendingValue)1092 public void markPending(EndPoint info, boolean pendingValue) { 1093 synchronized (mAuthorities) { 1094 AuthorityInfo authority = getOrCreateAuthorityLocked(info, 1095 -1 /* desired identifier */, 1096 true /* write accounts to storage */); 1097 if (authority == null) { 1098 return; 1099 } 1100 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); 1101 status.pending = pendingValue; 1102 } 1103 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING, info); 1104 } 1105 1106 /** 1107 * Called when the set of account has changed, given the new array of 1108 * active accounts. 1109 */ removeStaleAccounts(@ullable Account[] currentAccounts, int userId)1110 public void removeStaleAccounts(@Nullable Account[] currentAccounts, int userId) { 1111 synchronized (mAuthorities) { 1112 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1113 Slog.v(TAG, "Updating for new accounts..."); 1114 } 1115 SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>(); 1116 Iterator<AccountInfo> accIt = mAccounts.values().iterator(); 1117 while (accIt.hasNext()) { 1118 AccountInfo acc = accIt.next(); 1119 if (acc.accountAndUser.userId != userId) { 1120 continue; // Irrelevant user. 1121 } 1122 if ((currentAccounts == null) 1123 || !ArrayUtils.contains(currentAccounts, acc.accountAndUser.account)) { 1124 // This account no longer exists... 1125 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1126 Slog.v(TAG, "Account removed: " + acc.accountAndUser); 1127 } 1128 for (AuthorityInfo auth : acc.authorities.values()) { 1129 removing.put(auth.ident, auth); 1130 } 1131 accIt.remove(); 1132 } 1133 } 1134 1135 // Clean out all data structures. 1136 int i = removing.size(); 1137 if (i > 0) { 1138 while (i > 0) { 1139 i--; 1140 int ident = removing.keyAt(i); 1141 AuthorityInfo auth = removing.valueAt(i); 1142 if (mAuthorityRemovedListener != null) { 1143 mAuthorityRemovedListener.onAuthorityRemoved(auth.target); 1144 } 1145 mAuthorities.remove(ident); 1146 int j = mSyncStatus.size(); 1147 while (j > 0) { 1148 j--; 1149 if (mSyncStatus.keyAt(j) == ident) { 1150 mSyncStatus.remove(mSyncStatus.keyAt(j)); 1151 } 1152 } 1153 j = mSyncHistory.size(); 1154 while (j > 0) { 1155 j--; 1156 if (mSyncHistory.get(j).authorityId == ident) { 1157 mSyncHistory.remove(j); 1158 } 1159 } 1160 } 1161 writeAccountInfoLocked(); 1162 writeStatusLocked(); 1163 writeStatisticsLocked(); 1164 } 1165 } 1166 } 1167 1168 /** 1169 * Called when a sync is starting. Supply a valid ActiveSyncContext with information 1170 * about the sync. 1171 */ addActiveSync(SyncManager.ActiveSyncContext activeSyncContext)1172 public SyncInfo addActiveSync(SyncManager.ActiveSyncContext activeSyncContext) { 1173 final SyncInfo syncInfo; 1174 synchronized (mAuthorities) { 1175 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1176 Slog.v(TAG, "setActiveSync: account=" 1177 + " auth=" + activeSyncContext.mSyncOperation.target 1178 + " src=" + activeSyncContext.mSyncOperation.syncSource 1179 + " extras=" + activeSyncContext.mSyncOperation.getExtrasAsString()); 1180 } 1181 final EndPoint info = activeSyncContext.mSyncOperation.target; 1182 AuthorityInfo authorityInfo = getOrCreateAuthorityLocked( 1183 info, 1184 -1 /* assign a new identifier if creating a new target */, 1185 true /* write to storage if this results in a change */); 1186 syncInfo = new SyncInfo( 1187 authorityInfo.ident, 1188 authorityInfo.target.account, 1189 authorityInfo.target.provider, 1190 activeSyncContext.mStartTime); 1191 getCurrentSyncs(authorityInfo.target.userId).add(syncInfo); 1192 } 1193 reportActiveChange(activeSyncContext.mSyncOperation.target); 1194 return syncInfo; 1195 } 1196 1197 /** 1198 * Called to indicate that a previously active sync is no longer active. 1199 */ removeActiveSync(SyncInfo syncInfo, int userId)1200 public void removeActiveSync(SyncInfo syncInfo, int userId) { 1201 synchronized (mAuthorities) { 1202 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1203 Slog.v(TAG, "removeActiveSync: account=" + syncInfo.account 1204 + " user=" + userId 1205 + " auth=" + syncInfo.authority); 1206 } 1207 getCurrentSyncs(userId).remove(syncInfo); 1208 } 1209 1210 reportActiveChange(new EndPoint(syncInfo.account, syncInfo.authority, userId)); 1211 } 1212 1213 /** 1214 * To allow others to send active change reports, to poke clients. 1215 */ reportActiveChange(EndPoint target)1216 public void reportActiveChange(EndPoint target) { 1217 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, target); 1218 } 1219 1220 /** 1221 * Note that sync has started for the given operation. 1222 */ insertStartSyncEvent(SyncOperation op, long now)1223 public long insertStartSyncEvent(SyncOperation op, long now) { 1224 long id; 1225 synchronized (mAuthorities) { 1226 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1227 Slog.v(TAG, "insertStartSyncEvent: " + op); 1228 } 1229 AuthorityInfo authority = getAuthorityLocked(op.target, "insertStartSyncEvent"); 1230 if (authority == null) { 1231 return -1; 1232 } 1233 SyncHistoryItem item = new SyncHistoryItem(); 1234 item.initialization = op.isInitialization(); 1235 item.authorityId = authority.ident; 1236 item.historyId = mNextHistoryId++; 1237 if (mNextHistoryId < 0) mNextHistoryId = 0; 1238 item.eventTime = now; 1239 item.source = op.syncSource; 1240 item.reason = op.reason; 1241 item.extras = op.getClonedExtras(); 1242 item.event = EVENT_START; 1243 item.syncExemptionFlag = op.syncExemptionFlag; 1244 mSyncHistory.add(0, item); 1245 while (mSyncHistory.size() > MAX_HISTORY) { 1246 mSyncHistory.remove(mSyncHistory.size()-1); 1247 } 1248 id = item.historyId; 1249 if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "returning historyId " + id); 1250 } 1251 1252 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, op.owningPackage, op.target.userId); 1253 return id; 1254 } 1255 stopSyncEvent(long historyId, long elapsedTime, String resultMessage, long downstreamActivity, long upstreamActivity, String opPackageName, int userId)1256 public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage, 1257 long downstreamActivity, long upstreamActivity, String opPackageName, 1258 int userId) { 1259 synchronized (mAuthorities) { 1260 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1261 Slog.v(TAG, "stopSyncEvent: historyId=" + historyId); 1262 } 1263 SyncHistoryItem item = null; 1264 int i = mSyncHistory.size(); 1265 while (i > 0) { 1266 i--; 1267 item = mSyncHistory.get(i); 1268 if (item.historyId == historyId) { 1269 break; 1270 } 1271 item = null; 1272 } 1273 1274 if (item == null) { 1275 Slog.w(TAG, "stopSyncEvent: no history for id " + historyId); 1276 return; 1277 } 1278 1279 item.elapsedTime = elapsedTime; 1280 item.event = EVENT_STOP; 1281 item.mesg = resultMessage; 1282 item.downstreamActivity = downstreamActivity; 1283 item.upstreamActivity = upstreamActivity; 1284 1285 SyncStatusInfo status = getOrCreateSyncStatusLocked(item.authorityId); 1286 1287 status.maybeResetTodayStats(isClockValid(), /*force=*/ false); 1288 1289 status.totalStats.numSyncs++; 1290 status.todayStats.numSyncs++; 1291 status.totalStats.totalElapsedTime += elapsedTime; 1292 status.todayStats.totalElapsedTime += elapsedTime; 1293 switch (item.source) { 1294 case SOURCE_LOCAL: 1295 status.totalStats.numSourceLocal++; 1296 status.todayStats.numSourceLocal++; 1297 break; 1298 case SOURCE_POLL: 1299 status.totalStats.numSourcePoll++; 1300 status.todayStats.numSourcePoll++; 1301 break; 1302 case SOURCE_USER: 1303 status.totalStats.numSourceUser++; 1304 status.todayStats.numSourceUser++; 1305 break; 1306 case SOURCE_OTHER: 1307 status.totalStats.numSourceOther++; 1308 status.todayStats.numSourceOther++; 1309 break; 1310 case SOURCE_PERIODIC: 1311 status.totalStats.numSourcePeriodic++; 1312 status.todayStats.numSourcePeriodic++; 1313 break; 1314 case SOURCE_FEED: 1315 status.totalStats.numSourceFeed++; 1316 status.todayStats.numSourceFeed++; 1317 break; 1318 } 1319 1320 boolean writeStatisticsNow = false; 1321 int day = getCurrentDayLocked(); 1322 if (mDayStats[0] == null) { 1323 mDayStats[0] = new DayStats(day); 1324 } else if (day != mDayStats[0].day) { 1325 System.arraycopy(mDayStats, 0, mDayStats, 1, mDayStats.length-1); 1326 mDayStats[0] = new DayStats(day); 1327 writeStatisticsNow = true; 1328 } else if (mDayStats[0] == null) { 1329 } 1330 final DayStats ds = mDayStats[0]; 1331 1332 final long lastSyncTime = (item.eventTime + elapsedTime); 1333 boolean writeStatusNow = false; 1334 if (MESG_SUCCESS.equals(resultMessage)) { 1335 // - if successful, update the successful columns 1336 if (status.lastSuccessTime == 0 || status.lastFailureTime != 0) { 1337 writeStatusNow = true; 1338 } 1339 status.setLastSuccess(item.source, lastSyncTime); 1340 ds.successCount++; 1341 ds.successTime += elapsedTime; 1342 } else if (!MESG_CANCELED.equals(resultMessage)) { 1343 if (status.lastFailureTime == 0) { 1344 writeStatusNow = true; 1345 } 1346 status.totalStats.numFailures++; 1347 status.todayStats.numFailures++; 1348 1349 status.setLastFailure(item.source, lastSyncTime, resultMessage); 1350 1351 ds.failureCount++; 1352 ds.failureTime += elapsedTime; 1353 } else { 1354 // Cancel 1355 status.totalStats.numCancels++; 1356 status.todayStats.numCancels++; 1357 writeStatusNow = true; 1358 } 1359 final StringBuilder event = new StringBuilder(); 1360 event.append("" + resultMessage + " Source=" + SyncStorageEngine.SOURCES[item.source] 1361 + " Elapsed="); 1362 SyncManager.formatDurationHMS(event, elapsedTime); 1363 event.append(" Reason="); 1364 event.append(SyncOperation.reasonToString(null, item.reason)); 1365 if (item.syncExemptionFlag != ContentResolver.SYNC_EXEMPTION_NONE) { 1366 event.append(" Exemption="); 1367 switch (item.syncExemptionFlag) { 1368 case ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET: 1369 event.append("fg"); 1370 break; 1371 case ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP: 1372 event.append("top"); 1373 break; 1374 default: 1375 event.append(item.syncExemptionFlag); 1376 break; 1377 } 1378 } 1379 event.append(" Extras="); 1380 SyncOperation.extrasToStringBuilder(item.extras, event); 1381 1382 status.addEvent(event.toString()); 1383 1384 if (writeStatusNow) { 1385 writeStatusLocked(); 1386 } else if (!mHandler.hasMessages(MSG_WRITE_STATUS)) { 1387 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_WRITE_STATUS), 1388 WRITE_STATUS_DELAY); 1389 } 1390 if (writeStatisticsNow) { 1391 writeStatisticsLocked(); 1392 } else if (!mHandler.hasMessages(MSG_WRITE_STATISTICS)) { 1393 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_WRITE_STATISTICS), 1394 WRITE_STATISTICS_DELAY); 1395 } 1396 } 1397 1398 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, opPackageName, userId); 1399 } 1400 1401 /** 1402 * Return a list of the currently active syncs. Note that the returned 1403 * items are the real, live active sync objects, so be careful what you do 1404 * with it. 1405 */ getCurrentSyncs(int userId)1406 private List<SyncInfo> getCurrentSyncs(int userId) { 1407 synchronized (mAuthorities) { 1408 return getCurrentSyncsLocked(userId); 1409 } 1410 } 1411 1412 /** 1413 * @param userId Id of user to return current sync info. 1414 * @param canAccessAccounts Determines whether to redact Account information from the result. 1415 * @return a copy of the current syncs data structure. Will not return null. 1416 */ getCurrentSyncsCopy(int userId, boolean canAccessAccounts)1417 public List<SyncInfo> getCurrentSyncsCopy(int userId, boolean canAccessAccounts) { 1418 synchronized (mAuthorities) { 1419 final List<SyncInfo> syncs = getCurrentSyncsLocked(userId); 1420 final List<SyncInfo> syncsCopy = new ArrayList<SyncInfo>(); 1421 for (SyncInfo sync : syncs) { 1422 SyncInfo copy; 1423 if (!canAccessAccounts) { 1424 copy = SyncInfo.createAccountRedacted( 1425 sync.authorityId, sync.authority, sync.startTime); 1426 } else { 1427 copy = new SyncInfo(sync); 1428 } 1429 syncsCopy.add(copy); 1430 } 1431 return syncsCopy; 1432 } 1433 } 1434 getCurrentSyncsLocked(int userId)1435 private List<SyncInfo> getCurrentSyncsLocked(int userId) { 1436 ArrayList<SyncInfo> syncs = mCurrentSyncs.get(userId); 1437 if (syncs == null) { 1438 syncs = new ArrayList<SyncInfo>(); 1439 mCurrentSyncs.put(userId, syncs); 1440 } 1441 return syncs; 1442 } 1443 1444 /** 1445 * Return a copy of the specified target with the corresponding sync status 1446 */ getCopyOfAuthorityWithSyncStatus(EndPoint info)1447 public Pair<AuthorityInfo, SyncStatusInfo> getCopyOfAuthorityWithSyncStatus(EndPoint info) { 1448 synchronized (mAuthorities) { 1449 AuthorityInfo authorityInfo = getOrCreateAuthorityLocked(info, 1450 -1 /* assign a new identifier if creating a new target */, 1451 true /* write to storage if this results in a change */); 1452 return createCopyPairOfAuthorityWithSyncStatusLocked(authorityInfo); 1453 } 1454 } 1455 1456 /** 1457 * Returns the status that matches the target. 1458 * 1459 * @param info the endpoint target we are querying status info for. 1460 * @return the SyncStatusInfo for the endpoint. 1461 */ getStatusByAuthority(EndPoint info)1462 public SyncStatusInfo getStatusByAuthority(EndPoint info) { 1463 if (info.account == null || info.provider == null) { 1464 return null; 1465 } 1466 synchronized (mAuthorities) { 1467 final int N = mSyncStatus.size(); 1468 for (int i = 0; i < N; i++) { 1469 SyncStatusInfo cur = mSyncStatus.valueAt(i); 1470 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId); 1471 if (ainfo != null 1472 && ainfo.target.matchesSpec(info)) { 1473 return cur; 1474 } 1475 } 1476 return null; 1477 } 1478 } 1479 1480 /** Return true if the pending status is true of any matching authorities. */ isSyncPending(EndPoint info)1481 public boolean isSyncPending(EndPoint info) { 1482 synchronized (mAuthorities) { 1483 final int N = mSyncStatus.size(); 1484 for (int i = 0; i < N; i++) { 1485 SyncStatusInfo cur = mSyncStatus.valueAt(i); 1486 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId); 1487 if (ainfo == null) { 1488 continue; 1489 } 1490 if (!ainfo.target.matchesSpec(info)) { 1491 continue; 1492 } 1493 if (cur.pending) { 1494 return true; 1495 } 1496 } 1497 return false; 1498 } 1499 } 1500 1501 /** 1502 * Return an array of the current sync status for all authorities. Note 1503 * that the objects inside the array are the real, live status objects, 1504 * so be careful what you do with them. 1505 */ getSyncHistory()1506 public ArrayList<SyncHistoryItem> getSyncHistory() { 1507 synchronized (mAuthorities) { 1508 final int N = mSyncHistory.size(); 1509 ArrayList<SyncHistoryItem> items = new ArrayList<SyncHistoryItem>(N); 1510 for (int i=0; i<N; i++) { 1511 items.add(mSyncHistory.get(i)); 1512 } 1513 return items; 1514 } 1515 } 1516 1517 /** 1518 * Return an array of the current per-day statistics. Note 1519 * that the objects inside the array are the real, live status objects, 1520 * so be careful what you do with them. 1521 */ getDayStatistics()1522 public DayStats[] getDayStatistics() { 1523 synchronized (mAuthorities) { 1524 DayStats[] ds = new DayStats[mDayStats.length]; 1525 System.arraycopy(mDayStats, 0, ds, 0, ds.length); 1526 return ds; 1527 } 1528 } 1529 createCopyPairOfAuthorityWithSyncStatusLocked( AuthorityInfo authorityInfo)1530 private Pair<AuthorityInfo, SyncStatusInfo> createCopyPairOfAuthorityWithSyncStatusLocked( 1531 AuthorityInfo authorityInfo) { 1532 SyncStatusInfo syncStatusInfo = getOrCreateSyncStatusLocked(authorityInfo.ident); 1533 return Pair.create(new AuthorityInfo(authorityInfo), new SyncStatusInfo(syncStatusInfo)); 1534 } 1535 getCurrentDayLocked()1536 private int getCurrentDayLocked() { 1537 mCal.setTimeInMillis(System.currentTimeMillis()); 1538 final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR); 1539 if (mYear != mCal.get(Calendar.YEAR)) { 1540 mYear = mCal.get(Calendar.YEAR); 1541 mCal.clear(); 1542 mCal.set(Calendar.YEAR, mYear); 1543 mYearInDays = (int)(mCal.getTimeInMillis()/86400000); 1544 } 1545 return dayOfYear + mYearInDays; 1546 } 1547 1548 /** 1549 * Retrieve a target's full info, returning null if one does not exist. 1550 * 1551 * @param info info of the target to look up. 1552 * @param tag If non-null, this will be used in a log message if the 1553 * requested target does not exist. 1554 */ getAuthorityLocked(EndPoint info, String tag)1555 private AuthorityInfo getAuthorityLocked(EndPoint info, String tag) { 1556 AccountAndUser au = new AccountAndUser(info.account, info.userId); 1557 AccountInfo accountInfo = mAccounts.get(au); 1558 if (accountInfo == null) { 1559 if (tag != null) { 1560 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1561 Slog.v(TAG, tag + ": unknown account " + au); 1562 } 1563 } 1564 return null; 1565 } 1566 AuthorityInfo authority = accountInfo.authorities.get(info.provider); 1567 if (authority == null) { 1568 if (tag != null) { 1569 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1570 Slog.v(TAG, tag + ": unknown provider " + info.provider); 1571 } 1572 } 1573 return null; 1574 } 1575 return authority; 1576 } 1577 1578 /** 1579 * @param info info identifying target. 1580 * @param ident unique identifier for target. -1 for none. 1581 * @param doWrite if true, update the accounts.xml file on the disk. 1582 * @return the authority that corresponds to the provided sync target, creating it if none 1583 * exists. 1584 */ getOrCreateAuthorityLocked(EndPoint info, int ident, boolean doWrite)1585 private AuthorityInfo getOrCreateAuthorityLocked(EndPoint info, int ident, boolean doWrite) { 1586 AuthorityInfo authority = null; 1587 AccountAndUser au = new AccountAndUser(info.account, info.userId); 1588 AccountInfo account = mAccounts.get(au); 1589 if (account == null) { 1590 account = new AccountInfo(au); 1591 mAccounts.put(au, account); 1592 } 1593 authority = account.authorities.get(info.provider); 1594 if (authority == null) { 1595 authority = createAuthorityLocked(info, ident, doWrite); 1596 account.authorities.put(info.provider, authority); 1597 } 1598 return authority; 1599 } 1600 createAuthorityLocked(EndPoint info, int ident, boolean doWrite)1601 private AuthorityInfo createAuthorityLocked(EndPoint info, int ident, boolean doWrite) { 1602 AuthorityInfo authority; 1603 if (ident < 0) { 1604 ident = mNextAuthorityId; 1605 mNextAuthorityId++; 1606 doWrite = true; 1607 } 1608 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1609 Slog.v(TAG, "created a new AuthorityInfo for " + info); 1610 } 1611 authority = new AuthorityInfo(info, ident); 1612 mAuthorities.put(ident, authority); 1613 if (doWrite) { 1614 writeAccountInfoLocked(); 1615 } 1616 return authority; 1617 } 1618 removeAuthority(EndPoint info)1619 public void removeAuthority(EndPoint info) { 1620 synchronized (mAuthorities) { 1621 removeAuthorityLocked(info.account, info.userId, info.provider, true /* doWrite */); 1622 } 1623 } 1624 1625 /** 1626 * Remove an authority associated with a provider. Needs to be a standalone function for 1627 * backward compatibility. 1628 */ removeAuthorityLocked(Account account, int userId, String authorityName, boolean doWrite)1629 private void removeAuthorityLocked(Account account, int userId, String authorityName, 1630 boolean doWrite) { 1631 AccountInfo accountInfo = mAccounts.get(new AccountAndUser(account, userId)); 1632 if (accountInfo != null) { 1633 final AuthorityInfo authorityInfo = accountInfo.authorities.remove(authorityName); 1634 if (authorityInfo != null) { 1635 if (mAuthorityRemovedListener != null) { 1636 mAuthorityRemovedListener.onAuthorityRemoved(authorityInfo.target); 1637 } 1638 mAuthorities.remove(authorityInfo.ident); 1639 if (doWrite) { 1640 writeAccountInfoLocked(); 1641 } 1642 } 1643 } 1644 } 1645 getOrCreateSyncStatusLocked(int authorityId)1646 private SyncStatusInfo getOrCreateSyncStatusLocked(int authorityId) { 1647 SyncStatusInfo status = mSyncStatus.get(authorityId); 1648 if (status == null) { 1649 status = new SyncStatusInfo(authorityId); 1650 mSyncStatus.put(authorityId, status); 1651 } 1652 return status; 1653 } 1654 writeAllState()1655 public void writeAllState() { 1656 synchronized (mAuthorities) { 1657 // Account info is always written so no need to do it here. 1658 writeStatusLocked(); 1659 writeStatisticsLocked(); 1660 } 1661 } 1662 shouldGrantSyncAdaptersAccountAccess()1663 public boolean shouldGrantSyncAdaptersAccountAccess() { 1664 return mGrantSyncAdaptersAccountAccess; 1665 } 1666 1667 /** 1668 * public for testing 1669 */ clearAndReadState()1670 public void clearAndReadState() { 1671 synchronized (mAuthorities) { 1672 mAuthorities.clear(); 1673 mAccounts.clear(); 1674 mServices.clear(); 1675 mSyncStatus.clear(); 1676 mSyncHistory.clear(); 1677 1678 readAccountInfoLocked(); 1679 readStatusLocked(); 1680 readStatisticsLocked(); 1681 writeAccountInfoLocked(); 1682 writeStatusLocked(); 1683 writeStatisticsLocked(); 1684 } 1685 } 1686 1687 /** 1688 * Read all account information back in to the initial engine state. 1689 */ readAccountInfoLocked()1690 private void readAccountInfoLocked() { 1691 int highestAuthorityId = -1; 1692 FileInputStream fis = null; 1693 try { 1694 fis = mAccountInfoFile.openRead(); 1695 if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { 1696 Slog.v(TAG_FILE, "Reading " + mAccountInfoFile.getBaseFile()); 1697 } 1698 TypedXmlPullParser parser = Xml.resolvePullParser(fis); 1699 int eventType = parser.getEventType(); 1700 while (eventType != XmlPullParser.START_TAG && 1701 eventType != XmlPullParser.END_DOCUMENT) { 1702 eventType = parser.next(); 1703 } 1704 if (eventType == XmlPullParser.END_DOCUMENT) { 1705 Slog.i(TAG, "No initial accounts"); 1706 return; 1707 } 1708 1709 String tagName = parser.getName(); 1710 if ("accounts".equals(tagName)) { 1711 boolean listen = parser.getAttributeBoolean( 1712 null, XML_ATTR_LISTEN_FOR_TICKLES, true); 1713 int version = parser.getAttributeInt(null, "version", 0); 1714 1715 if (version < 3) { 1716 mGrantSyncAdaptersAccountAccess = true; 1717 } 1718 1719 int nextId = parser.getAttributeInt(null, XML_ATTR_NEXT_AUTHORITY_ID, 0); 1720 mNextAuthorityId = Math.max(mNextAuthorityId, nextId); 1721 1722 mSyncRandomOffset = parser.getAttributeInt(null, XML_ATTR_SYNC_RANDOM_OFFSET, 0); 1723 if (mSyncRandomOffset == 0) { 1724 Random random = new Random(System.currentTimeMillis()); 1725 mSyncRandomOffset = random.nextInt(86400); 1726 } 1727 mMasterSyncAutomatically.put(0, listen); 1728 eventType = parser.next(); 1729 AuthorityInfo authority = null; 1730 PeriodicSync periodicSync = null; 1731 AccountAuthorityValidator validator = new AccountAuthorityValidator(mContext); 1732 do { 1733 if (eventType == XmlPullParser.START_TAG) { 1734 tagName = parser.getName(); 1735 if (parser.getDepth() == 2) { 1736 if ("authority".equals(tagName)) { 1737 authority = parseAuthority(parser, version, validator); 1738 periodicSync = null; 1739 if (authority != null) { 1740 if (authority.ident > highestAuthorityId) { 1741 highestAuthorityId = authority.ident; 1742 } 1743 } else { 1744 EventLog.writeEvent(0x534e4554, "26513719", -1, 1745 "Malformed authority"); 1746 } 1747 } else if (XML_TAG_LISTEN_FOR_TICKLES.equals(tagName)) { 1748 parseListenForTickles(parser); 1749 } 1750 } else if (parser.getDepth() == 3) { 1751 if ("periodicSync".equals(tagName) && authority != null) { 1752 periodicSync = parsePeriodicSync(parser, authority); 1753 } 1754 } else if (parser.getDepth() == 4 && periodicSync != null) { 1755 if ("extra".equals(tagName)) { 1756 parseExtra(parser, periodicSync.extras); 1757 } 1758 } 1759 } 1760 eventType = parser.next(); 1761 } while (eventType != XmlPullParser.END_DOCUMENT); 1762 } 1763 } catch (XmlPullParserException | ArrayIndexOutOfBoundsException e) { 1764 Slog.w(TAG, "Error reading accounts", e); 1765 return; 1766 } catch (java.io.IOException e) { 1767 if (fis == null) Slog.i(TAG, "No initial accounts"); 1768 else Slog.w(TAG, "Error reading accounts", e); 1769 return; 1770 } finally { 1771 mNextAuthorityId = Math.max(highestAuthorityId + 1, mNextAuthorityId); 1772 if (fis != null) { 1773 try { 1774 fis.close(); 1775 } catch (java.io.IOException e1) { 1776 } 1777 } 1778 } 1779 1780 maybeMigrateSettingsForRenamedAuthorities(); 1781 } 1782 1783 /** 1784 * Ensure the old pending.bin is deleted, as it has been changed to pending.xml. 1785 * pending.xml was used starting in KitKat. 1786 * @param syncDir directory where the sync files are located. 1787 */ maybeDeleteLegacyPendingInfoLocked(File syncDir)1788 private void maybeDeleteLegacyPendingInfoLocked(File syncDir) { 1789 File file = new File(syncDir, "pending.bin"); 1790 if (!file.exists()) { 1791 return; 1792 } else { 1793 file.delete(); 1794 } 1795 } 1796 1797 /** 1798 * some authority names have changed. copy over their settings and delete the old ones 1799 * @return true if a change was made 1800 */ maybeMigrateSettingsForRenamedAuthorities()1801 private boolean maybeMigrateSettingsForRenamedAuthorities() { 1802 boolean writeNeeded = false; 1803 1804 ArrayList<AuthorityInfo> authoritiesToRemove = new ArrayList<AuthorityInfo>(); 1805 final int N = mAuthorities.size(); 1806 for (int i = 0; i < N; i++) { 1807 AuthorityInfo authority = mAuthorities.valueAt(i); 1808 // skip this authority if it isn't one of the renamed ones 1809 final String newAuthorityName = sAuthorityRenames.get(authority.target.provider); 1810 if (newAuthorityName == null) { 1811 continue; 1812 } 1813 1814 // remember this authority so we can remove it later. we can't remove it 1815 // now without messing up this loop iteration 1816 authoritiesToRemove.add(authority); 1817 1818 // this authority isn't enabled, no need to copy it to the new authority name since 1819 // the default is "disabled" 1820 if (!authority.enabled) { 1821 continue; 1822 } 1823 1824 // if we already have a record of this new authority then don't copy over the settings 1825 EndPoint newInfo = 1826 new EndPoint(authority.target.account, 1827 newAuthorityName, 1828 authority.target.userId); 1829 if (getAuthorityLocked(newInfo, "cleanup") != null) { 1830 continue; 1831 } 1832 1833 AuthorityInfo newAuthority = 1834 getOrCreateAuthorityLocked(newInfo, -1 /* ident */, false /* doWrite */); 1835 newAuthority.enabled = true; 1836 writeNeeded = true; 1837 } 1838 1839 for (AuthorityInfo authorityInfo : authoritiesToRemove) { 1840 removeAuthorityLocked( 1841 authorityInfo.target.account, 1842 authorityInfo.target.userId, 1843 authorityInfo.target.provider, 1844 false /* doWrite */); 1845 writeNeeded = true; 1846 } 1847 1848 return writeNeeded; 1849 } 1850 parseListenForTickles(TypedXmlPullParser parser)1851 private void parseListenForTickles(TypedXmlPullParser parser) { 1852 int userId = 0; 1853 try { 1854 userId = parser.getAttributeInt(null, XML_ATTR_USER); 1855 } catch (XmlPullParserException e) { 1856 Slog.e(TAG, "error parsing the user for listen-for-tickles", e); 1857 } 1858 boolean listen = parser.getAttributeBoolean(null, XML_ATTR_ENABLED, true); 1859 mMasterSyncAutomatically.put(userId, listen); 1860 } 1861 parseAuthority(TypedXmlPullParser parser, int version, AccountAuthorityValidator validator)1862 private AuthorityInfo parseAuthority(TypedXmlPullParser parser, int version, 1863 AccountAuthorityValidator validator) throws XmlPullParserException { 1864 AuthorityInfo authority = null; 1865 int id = -1; 1866 try { 1867 id = parser.getAttributeInt(null, "id"); 1868 } catch (XmlPullParserException e) { 1869 Slog.e(TAG, "error parsing the id of the authority", e); 1870 } 1871 if (id >= 0) { 1872 String authorityName = parser.getAttributeValue(null, "authority"); 1873 boolean enabled = parser.getAttributeBoolean(null, XML_ATTR_ENABLED, true); 1874 String syncable = parser.getAttributeValue(null, "syncable"); 1875 String accountName = parser.getAttributeValue(null, "account"); 1876 String accountType = parser.getAttributeValue(null, "type"); 1877 int userId = parser.getAttributeInt(null, XML_ATTR_USER, 0); 1878 String packageName = parser.getAttributeValue(null, "package"); 1879 String className = parser.getAttributeValue(null, "class"); 1880 if (accountType == null && packageName == null) { 1881 accountType = "com.google"; 1882 syncable = String.valueOf(AuthorityInfo.NOT_INITIALIZED); 1883 } 1884 authority = mAuthorities.get(id); 1885 if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { 1886 Slog.v(TAG_FILE, "Adding authority:" 1887 + " account=" + accountName 1888 + " accountType=" + accountType 1889 + " auth=" + authorityName 1890 + " package=" + packageName 1891 + " class=" + className 1892 + " user=" + userId 1893 + " enabled=" + enabled 1894 + " syncable=" + syncable); 1895 } 1896 if (authority == null) { 1897 if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { 1898 Slog.v(TAG_FILE, "Creating authority entry"); 1899 } 1900 if (accountName != null && authorityName != null) { 1901 EndPoint info = new EndPoint( 1902 new Account(accountName, accountType), 1903 authorityName, userId); 1904 if (validator.isAccountValid(info.account, userId) 1905 && validator.isAuthorityValid(authorityName, userId)) { 1906 authority = getOrCreateAuthorityLocked(info, id, false); 1907 // If the version is 0 then we are upgrading from a file format that did not 1908 // know about periodic syncs. In that case don't clear the list since we 1909 // want the default, which is a daily periodic sync. 1910 // Otherwise clear out this default list since we will populate it later 1911 // with 1912 // the periodic sync descriptions that are read from the configuration file. 1913 if (version > 0) { 1914 authority.periodicSyncs.clear(); 1915 } 1916 } else { 1917 EventLog.writeEvent(0x534e4554, "35028827", -1, 1918 "account:" + info.account + " provider:" + authorityName + " user:" 1919 + userId); 1920 } 1921 } 1922 } 1923 if (authority != null) { 1924 authority.enabled = enabled; 1925 try { 1926 authority.syncable = (syncable == null) ? 1927 AuthorityInfo.NOT_INITIALIZED : Integer.parseInt(syncable); 1928 } catch (NumberFormatException e) { 1929 // On L we stored this as {"unknown", "true", "false"} so fall back to this 1930 // format. 1931 if ("unknown".equals(syncable)) { 1932 authority.syncable = AuthorityInfo.NOT_INITIALIZED; 1933 } else { 1934 authority.syncable = Boolean.parseBoolean(syncable) ? 1935 AuthorityInfo.SYNCABLE : AuthorityInfo.NOT_SYNCABLE; 1936 } 1937 1938 } 1939 } else { 1940 Slog.w(TAG, "Failure adding authority:" 1941 + " auth=" + authorityName 1942 + " enabled=" + enabled 1943 + " syncable=" + syncable); 1944 } 1945 } 1946 return authority; 1947 } 1948 1949 /** 1950 * Parse a periodic sync from accounts.xml. Sets the bundle to be empty. 1951 */ parsePeriodicSync(TypedXmlPullParser parser, AuthorityInfo authorityInfo)1952 private PeriodicSync parsePeriodicSync(TypedXmlPullParser parser, AuthorityInfo authorityInfo) { 1953 Bundle extras = new Bundle(); // Gets filled in later. 1954 long period; 1955 long flextime; 1956 try { 1957 period = parser.getAttributeLong(null, "period"); 1958 } catch (XmlPullParserException e) { 1959 Slog.e(TAG, "error parsing the period of a periodic sync", e); 1960 return null; 1961 } 1962 try { 1963 flextime = parser.getAttributeLong(null, "flex"); 1964 } catch (XmlPullParserException e) { 1965 flextime = calculateDefaultFlexTime(period); 1966 Slog.e(TAG, "Error formatting value parsed for periodic sync flex, using default: " 1967 + flextime, e); 1968 } 1969 PeriodicSync periodicSync; 1970 periodicSync = 1971 new PeriodicSync(authorityInfo.target.account, 1972 authorityInfo.target.provider, 1973 extras, 1974 period, flextime); 1975 authorityInfo.periodicSyncs.add(periodicSync); 1976 return periodicSync; 1977 } 1978 parseExtra(TypedXmlPullParser parser, Bundle extras)1979 private void parseExtra(TypedXmlPullParser parser, Bundle extras) { 1980 String name = parser.getAttributeValue(null, "name"); 1981 String type = parser.getAttributeValue(null, "type"); 1982 1983 try { 1984 if ("long".equals(type)) { 1985 extras.putLong(name, parser.getAttributeLong(null, "value1")); 1986 } else if ("integer".equals(type)) { 1987 extras.putInt(name, parser.getAttributeInt(null, "value1")); 1988 } else if ("double".equals(type)) { 1989 extras.putDouble(name, parser.getAttributeDouble(null, "value1")); 1990 } else if ("float".equals(type)) { 1991 extras.putFloat(name, parser.getAttributeFloat(null, "value1")); 1992 } else if ("boolean".equals(type)) { 1993 extras.putBoolean(name, parser.getAttributeBoolean(null, "value1")); 1994 } else if ("string".equals(type)) { 1995 extras.putString(name, parser.getAttributeValue(null, "value1")); 1996 } else if ("account".equals(type)) { 1997 final String value1 = parser.getAttributeValue(null, "value1"); 1998 final String value2 = parser.getAttributeValue(null, "value2"); 1999 extras.putParcelable(name, new Account(value1, value2)); 2000 } 2001 } catch (XmlPullParserException e) { 2002 Slog.e(TAG, "error parsing bundle value", e); 2003 } 2004 } 2005 2006 /** 2007 * Write all account information to the account file. 2008 */ writeAccountInfoLocked()2009 private void writeAccountInfoLocked() { 2010 if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { 2011 Slog.v(TAG_FILE, "Writing new " + mAccountInfoFile.getBaseFile()); 2012 } 2013 FileOutputStream fos = null; 2014 2015 try { 2016 fos = mAccountInfoFile.startWrite(); 2017 TypedXmlSerializer out = Xml.resolveSerializer(fos); 2018 out.startDocument(null, true); 2019 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 2020 2021 out.startTag(null, "accounts"); 2022 out.attributeInt(null, "version", ACCOUNTS_VERSION); 2023 out.attributeInt(null, XML_ATTR_NEXT_AUTHORITY_ID, mNextAuthorityId); 2024 out.attributeInt(null, XML_ATTR_SYNC_RANDOM_OFFSET, mSyncRandomOffset); 2025 2026 // Write the Sync Automatically flags for each user 2027 final int M = mMasterSyncAutomatically.size(); 2028 for (int m = 0; m < M; m++) { 2029 int userId = mMasterSyncAutomatically.keyAt(m); 2030 Boolean listen = mMasterSyncAutomatically.valueAt(m); 2031 out.startTag(null, XML_TAG_LISTEN_FOR_TICKLES); 2032 out.attributeInt(null, XML_ATTR_USER, userId); 2033 out.attributeBoolean(null, XML_ATTR_ENABLED, listen); 2034 out.endTag(null, XML_TAG_LISTEN_FOR_TICKLES); 2035 } 2036 2037 final int N = mAuthorities.size(); 2038 for (int i = 0; i < N; i++) { 2039 AuthorityInfo authority = mAuthorities.valueAt(i); 2040 EndPoint info = authority.target; 2041 out.startTag(null, "authority"); 2042 out.attributeInt(null, "id", authority.ident); 2043 out.attributeInt(null, XML_ATTR_USER, info.userId); 2044 out.attributeBoolean(null, XML_ATTR_ENABLED, authority.enabled); 2045 out.attribute(null, "account", info.account.name); 2046 out.attribute(null, "type", info.account.type); 2047 out.attribute(null, "authority", info.provider); 2048 out.attributeInt(null, "syncable", authority.syncable); 2049 out.endTag(null, "authority"); 2050 } 2051 out.endTag(null, "accounts"); 2052 out.endDocument(); 2053 mAccountInfoFile.finishWrite(fos); 2054 } catch (java.io.IOException e1) { 2055 Slog.w(TAG, "Error writing accounts", e1); 2056 if (fos != null) { 2057 mAccountInfoFile.failWrite(fos); 2058 } 2059 } 2060 } 2061 2062 public static final int STATUS_FILE_END = 0; 2063 public static final int STATUS_FILE_ITEM = 100; 2064 readStatusParcelLocked(File parcel)2065 private void readStatusParcelLocked(File parcel) { 2066 try { 2067 final AtomicFile parcelFile = new AtomicFile(parcel); 2068 byte[] data = parcelFile.readFully(); 2069 Parcel in = Parcel.obtain(); 2070 in.unmarshall(data, 0, data.length); 2071 in.setDataPosition(0); 2072 int token; 2073 while ((token=in.readInt()) != STATUS_FILE_END) { 2074 if (token == STATUS_FILE_ITEM) { 2075 try { 2076 SyncStatusInfo status = new SyncStatusInfo(in); 2077 if (mAuthorities.indexOfKey(status.authorityId) >= 0) { 2078 status.pending = false; 2079 mSyncStatus.put(status.authorityId, status); 2080 } 2081 } catch (Exception e) { 2082 Slog.e(TAG, "Unable to parse some sync status.", e); 2083 } 2084 } else { 2085 // Ooops. 2086 Slog.w(TAG, "Unknown status token: " + token); 2087 break; 2088 } 2089 } 2090 } catch (IOException e) { 2091 Slog.i(TAG, "No initial status"); 2092 } 2093 } 2094 upgradeStatusIfNeededLocked()2095 private void upgradeStatusIfNeededLocked() { 2096 final File parcelStatus = new File(mSyncDir, LEGACY_STATUS_FILE_NAME); 2097 if (parcelStatus.exists() && !mStatusFile.exists()) { 2098 readStatusParcelLocked(parcelStatus); 2099 writeStatusLocked(); 2100 } 2101 2102 // if upgrade to proto was successful, delete parcel file 2103 if (DELETE_LEGACY_PARCEL_FILES && parcelStatus.exists() && mStatusFile.exists()) { 2104 parcelStatus.delete(); 2105 } 2106 } 2107 2108 /** 2109 * Read all sync status back in to the initial engine state. 2110 */ 2111 @VisibleForTesting readStatusLocked()2112 void readStatusLocked() { 2113 upgradeStatusIfNeededLocked(); 2114 2115 if (!mStatusFile.exists()) { 2116 return; 2117 } 2118 try { 2119 try (FileInputStream in = mStatusFile.openRead()) { 2120 readStatusInfoLocked(in); 2121 } 2122 } catch (Exception e) { 2123 Slog.e(TAG, "Unable to read status info file.", e); 2124 } 2125 } 2126 readStatusInfoLocked(InputStream in)2127 private void readStatusInfoLocked(InputStream in) throws IOException { 2128 final ProtoInputStream proto = new ProtoInputStream(in); 2129 while (true) { 2130 switch (proto.nextField()) { 2131 case (int) SyncStatusProto.STATUS: 2132 final long token = proto.start(SyncStatusProto.STATUS); 2133 final SyncStatusInfo status = readSyncStatusInfoLocked(proto); 2134 proto.end(token); 2135 if (mAuthorities.indexOfKey(status.authorityId) >= 0) { 2136 status.pending = false; 2137 mSyncStatus.put(status.authorityId, status); 2138 } 2139 break; 2140 case (int) SyncStatusProto.IS_JOB_NAMESPACE_MIGRATED: 2141 mIsJobNamespaceMigrated = 2142 proto.readBoolean(SyncStatusProto.IS_JOB_NAMESPACE_MIGRATED); 2143 break; 2144 case (int) SyncStatusProto.IS_JOB_ATTRIBUTION_FIXED: 2145 mIsJobAttributionFixed = 2146 proto.readBoolean(SyncStatusProto.IS_JOB_ATTRIBUTION_FIXED); 2147 break; 2148 case ProtoInputStream.NO_MORE_FIELDS: 2149 return; 2150 } 2151 } 2152 } 2153 readSyncStatusInfoLocked(ProtoInputStream proto)2154 private SyncStatusInfo readSyncStatusInfoLocked(ProtoInputStream proto) throws IOException { 2155 SyncStatusInfo status; 2156 if (proto.nextField(SyncStatusProto.StatusInfo.AUTHORITY_ID)) { 2157 //fast-path; this should work for most cases since the authority id is written first 2158 status = new SyncStatusInfo(proto.readInt(SyncStatusProto.StatusInfo.AUTHORITY_ID)); 2159 } else { 2160 // placeholder to read other data; assume the default authority id as 0 2161 status = new SyncStatusInfo(0); 2162 } 2163 2164 int successTimesCount = 0; 2165 int failureTimesCount = 0; 2166 ArrayList<Pair<Long, String>> lastEventInformation = new ArrayList<>(); 2167 while (true) { 2168 switch (proto.nextField()) { 2169 case (int) SyncStatusProto.StatusInfo.AUTHORITY_ID: 2170 // fast-path failed for some reason, rebuild the status from placeholder object 2171 Slog.w(TAG, "Failed to read the authority id via fast-path; " 2172 + "some data might not have been read."); 2173 status = new SyncStatusInfo( 2174 proto.readInt(SyncStatusProto.StatusInfo.AUTHORITY_ID), status); 2175 break; 2176 case (int) SyncStatusProto.StatusInfo.LAST_SUCCESS_TIME: 2177 status.lastSuccessTime = proto.readLong( 2178 SyncStatusProto.StatusInfo.LAST_SUCCESS_TIME); 2179 break; 2180 case (int) SyncStatusProto.StatusInfo.LAST_SUCCESS_SOURCE: 2181 status.lastSuccessSource = proto.readInt( 2182 SyncStatusProto.StatusInfo.LAST_SUCCESS_SOURCE); 2183 break; 2184 case (int) SyncStatusProto.StatusInfo.LAST_FAILURE_TIME: 2185 status.lastFailureTime = proto.readLong( 2186 SyncStatusProto.StatusInfo.LAST_FAILURE_TIME); 2187 break; 2188 case (int) SyncStatusProto.StatusInfo.LAST_FAILURE_SOURCE: 2189 status.lastFailureSource = proto.readInt( 2190 SyncStatusProto.StatusInfo.LAST_FAILURE_SOURCE); 2191 break; 2192 case (int) SyncStatusProto.StatusInfo.LAST_FAILURE_MESSAGE: 2193 status.lastFailureMesg = proto.readString( 2194 SyncStatusProto.StatusInfo.LAST_FAILURE_MESSAGE); 2195 break; 2196 case (int) SyncStatusProto.StatusInfo.INITIAL_FAILURE_TIME: 2197 status.initialFailureTime = proto.readLong( 2198 SyncStatusProto.StatusInfo.INITIAL_FAILURE_TIME); 2199 break; 2200 case (int) SyncStatusProto.StatusInfo.PENDING: 2201 status.pending = proto.readBoolean(SyncStatusProto.StatusInfo.PENDING); 2202 break; 2203 case (int) SyncStatusProto.StatusInfo.INITIALIZE: 2204 status.initialize = proto.readBoolean(SyncStatusProto.StatusInfo.INITIALIZE); 2205 break; 2206 case (int) SyncStatusProto.StatusInfo.PERIODIC_SYNC_TIMES: 2207 status.addPeriodicSyncTime( 2208 proto.readLong(SyncStatusProto.StatusInfo.PERIODIC_SYNC_TIMES)); 2209 break; 2210 case (int) SyncStatusProto.StatusInfo.LAST_EVENT_INFO: 2211 final long eventToken = proto.start(SyncStatusProto.StatusInfo.LAST_EVENT_INFO); 2212 final Pair<Long, String> lastEventInfo = parseLastEventInfoLocked(proto); 2213 if (lastEventInfo != null) { 2214 lastEventInformation.add(lastEventInfo); 2215 } 2216 proto.end(eventToken); 2217 break; 2218 case (int) SyncStatusProto.StatusInfo.LAST_TODAY_RESET_TIME: 2219 status.lastTodayResetTime = proto.readLong( 2220 SyncStatusProto.StatusInfo.LAST_TODAY_RESET_TIME); 2221 break; 2222 case (int) SyncStatusProto.StatusInfo.TOTAL_STATS: 2223 final long totalStatsToken = proto.start( 2224 SyncStatusProto.StatusInfo.TOTAL_STATS); 2225 readSyncStatusStatsLocked(proto, status.totalStats); 2226 proto.end(totalStatsToken); 2227 break; 2228 case (int) SyncStatusProto.StatusInfo.TODAY_STATS: 2229 final long todayStatsToken = proto.start( 2230 SyncStatusProto.StatusInfo.TODAY_STATS); 2231 readSyncStatusStatsLocked(proto, status.todayStats); 2232 proto.end(todayStatsToken); 2233 break; 2234 case (int) SyncStatusProto.StatusInfo.YESTERDAY_STATS: 2235 final long yesterdayStatsToken = proto.start( 2236 SyncStatusProto.StatusInfo.YESTERDAY_STATS); 2237 readSyncStatusStatsLocked(proto, status.yesterdayStats); 2238 proto.end(yesterdayStatsToken); 2239 break; 2240 case (int) SyncStatusProto.StatusInfo.PER_SOURCE_LAST_SUCCESS_TIMES: 2241 final long successTime = proto.readLong( 2242 SyncStatusProto.StatusInfo.PER_SOURCE_LAST_SUCCESS_TIMES); 2243 if (successTimesCount == status.perSourceLastSuccessTimes.length) { 2244 Slog.w(TAG, "Attempted to read more per source last success times " 2245 + "than expected; data might be corrupted."); 2246 break; 2247 } 2248 status.perSourceLastSuccessTimes[successTimesCount] = successTime; 2249 successTimesCount++; 2250 break; 2251 case (int) SyncStatusProto.StatusInfo.PER_SOURCE_LAST_FAILURE_TIMES: 2252 final long failureTime = proto.readLong( 2253 SyncStatusProto.StatusInfo.PER_SOURCE_LAST_FAILURE_TIMES); 2254 if (failureTimesCount == status.perSourceLastFailureTimes.length) { 2255 Slog.w(TAG, "Attempted to read more per source last failure times " 2256 + "than expected; data might be corrupted."); 2257 break; 2258 } 2259 status.perSourceLastFailureTimes[failureTimesCount] = failureTime; 2260 failureTimesCount++; 2261 break; 2262 case ProtoInputStream.NO_MORE_FIELDS: 2263 status.populateLastEventsInformation(lastEventInformation); 2264 return status; 2265 } 2266 } 2267 } 2268 parseLastEventInfoLocked(ProtoInputStream proto)2269 private Pair<Long, String> parseLastEventInfoLocked(ProtoInputStream proto) throws IOException { 2270 long time = 0; 2271 String message = null; 2272 while (true) { 2273 switch (proto.nextField()) { 2274 case (int) SyncStatusProto.StatusInfo.LastEventInfo.LAST_EVENT_TIME: 2275 time = proto.readLong(SyncStatusProto.StatusInfo.LastEventInfo.LAST_EVENT_TIME); 2276 break; 2277 case (int) SyncStatusProto.StatusInfo.LastEventInfo.LAST_EVENT: 2278 message = proto.readString(SyncStatusProto.StatusInfo.LastEventInfo.LAST_EVENT); 2279 break; 2280 case ProtoInputStream.NO_MORE_FIELDS: 2281 return message == null ? null : new Pair<>(time, message); 2282 } 2283 } 2284 } 2285 readSyncStatusStatsLocked(ProtoInputStream proto, SyncStatusInfo.Stats stats)2286 private void readSyncStatusStatsLocked(ProtoInputStream proto, SyncStatusInfo.Stats stats) 2287 throws IOException { 2288 while (true) { 2289 switch (proto.nextField()) { 2290 case (int) SyncStatusProto.StatusInfo.Stats.TOTAL_ELAPSED_TIME: 2291 stats.totalElapsedTime = proto.readLong( 2292 SyncStatusProto.StatusInfo.Stats.TOTAL_ELAPSED_TIME); 2293 break; 2294 case (int) SyncStatusProto.StatusInfo.Stats.NUM_SYNCS: 2295 stats.numSyncs = proto.readInt(SyncStatusProto.StatusInfo.Stats.NUM_SYNCS); 2296 break; 2297 case (int) SyncStatusProto.StatusInfo.Stats.NUM_FAILURES: 2298 stats.numFailures = proto.readInt( 2299 SyncStatusProto.StatusInfo.Stats.NUM_FAILURES); 2300 break; 2301 case (int) SyncStatusProto.StatusInfo.Stats.NUM_CANCELS: 2302 stats.numCancels = proto.readInt(SyncStatusProto.StatusInfo.Stats.NUM_CANCELS); 2303 break; 2304 case (int) SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_OTHER: 2305 stats.numSourceOther = proto.readInt( 2306 SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_OTHER); 2307 break; 2308 case (int) SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_LOCAL: 2309 stats.numSourceLocal = proto.readInt( 2310 SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_LOCAL); 2311 break; 2312 case (int) SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_POLL: 2313 stats.numSourcePoll = proto.readInt( 2314 SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_POLL); 2315 break; 2316 case (int) SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_USER: 2317 stats.numSourceUser = proto.readInt( 2318 SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_USER); 2319 break; 2320 case (int) SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_PERIODIC: 2321 stats.numSourcePeriodic = proto.readInt( 2322 SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_PERIODIC); 2323 break; 2324 case (int) SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_FEED: 2325 stats.numSourceFeed = proto.readInt( 2326 SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_FEED); 2327 break; 2328 case ProtoInputStream.NO_MORE_FIELDS: 2329 return; 2330 } 2331 } 2332 } 2333 2334 /** 2335 * Write all sync status to the sync status file. 2336 */ 2337 @VisibleForTesting writeStatusLocked()2338 void writeStatusLocked() { 2339 if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { 2340 Slog.v(TAG_FILE, "Writing new " + mStatusFile.getBaseFile()); 2341 } 2342 2343 // The file is being written, so we don't need to have a scheduled 2344 // write until the next change. 2345 mHandler.removeMessages(MSG_WRITE_STATUS); 2346 2347 FileOutputStream fos = null; 2348 try { 2349 fos = mStatusFile.startWrite(); 2350 writeStatusInfoLocked(fos); 2351 mStatusFile.finishWrite(fos); 2352 fos = null; 2353 } catch (IOException | IllegalArgumentException e) { 2354 Slog.e(TAG, "Unable to write sync status to proto.", e); 2355 } finally { 2356 // when fos is null (successful write), this is a no-op. 2357 mStatusFile.failWrite(fos); 2358 } 2359 } 2360 writeStatusInfoLocked(OutputStream out)2361 private void writeStatusInfoLocked(OutputStream out) { 2362 final ProtoOutputStream proto = new ProtoOutputStream(out); 2363 final int size = mSyncStatus.size(); 2364 for (int i = 0; i < size; i++) { 2365 final SyncStatusInfo info = mSyncStatus.valueAt(i); 2366 final long token = proto.start(SyncStatusProto.STATUS); 2367 // authority id should be written first to take advantage of the fast path in read 2368 proto.write(SyncStatusProto.StatusInfo.AUTHORITY_ID, info.authorityId); 2369 proto.write(SyncStatusProto.StatusInfo.LAST_SUCCESS_TIME, info.lastSuccessTime); 2370 proto.write(SyncStatusProto.StatusInfo.LAST_SUCCESS_SOURCE, info.lastSuccessSource); 2371 proto.write(SyncStatusProto.StatusInfo.LAST_FAILURE_TIME, info.lastFailureTime); 2372 proto.write(SyncStatusProto.StatusInfo.LAST_FAILURE_SOURCE, info.lastFailureSource); 2373 proto.write(SyncStatusProto.StatusInfo.LAST_FAILURE_MESSAGE, info.lastFailureMesg); 2374 proto.write(SyncStatusProto.StatusInfo.INITIAL_FAILURE_TIME, info.initialFailureTime); 2375 proto.write(SyncStatusProto.StatusInfo.PENDING, info.pending); 2376 proto.write(SyncStatusProto.StatusInfo.INITIALIZE, info.initialize); 2377 final int periodicSyncTimesSize = info.getPeriodicSyncTimesSize(); 2378 for (int j = 0; j < periodicSyncTimesSize; j++) { 2379 proto.write(SyncStatusProto.StatusInfo.PERIODIC_SYNC_TIMES, 2380 info.getPeriodicSyncTime(j)); 2381 } 2382 final int lastEventsSize = info.getEventCount(); 2383 for (int j = 0; j < lastEventsSize; j++) { 2384 final long eventToken = proto.start(SyncStatusProto.StatusInfo.LAST_EVENT_INFO); 2385 proto.write(SyncStatusProto.StatusInfo.LastEventInfo.LAST_EVENT_TIME, 2386 info.getEventTime(j)); 2387 proto.write(SyncStatusProto.StatusInfo.LastEventInfo.LAST_EVENT, info.getEvent(j)); 2388 proto.end(eventToken); 2389 } 2390 proto.write(SyncStatusProto.StatusInfo.LAST_TODAY_RESET_TIME, info.lastTodayResetTime); 2391 2392 final long totalStatsToken = proto.start(SyncStatusProto.StatusInfo.TOTAL_STATS); 2393 writeStatusStatsLocked(proto, info.totalStats); 2394 proto.end(totalStatsToken); 2395 final long todayStatsToken = proto.start(SyncStatusProto.StatusInfo.TODAY_STATS); 2396 writeStatusStatsLocked(proto, info.todayStats); 2397 proto.end(todayStatsToken); 2398 final long yesterdayStatsToken = proto.start( 2399 SyncStatusProto.StatusInfo.YESTERDAY_STATS); 2400 writeStatusStatsLocked(proto, info.yesterdayStats); 2401 proto.end(yesterdayStatsToken); 2402 2403 final int lastSuccessTimesSize = info.perSourceLastSuccessTimes.length; 2404 for (int j = 0; j < lastSuccessTimesSize; j++) { 2405 proto.write(SyncStatusProto.StatusInfo.PER_SOURCE_LAST_SUCCESS_TIMES, 2406 info.perSourceLastSuccessTimes[j]); 2407 } 2408 final int lastFailureTimesSize = info.perSourceLastFailureTimes.length; 2409 for (int j = 0; j < lastFailureTimesSize; j++) { 2410 proto.write(SyncStatusProto.StatusInfo.PER_SOURCE_LAST_FAILURE_TIMES, 2411 info.perSourceLastFailureTimes[j]); 2412 } 2413 proto.end(token); 2414 } 2415 2416 proto.write(SyncStatusProto.IS_JOB_NAMESPACE_MIGRATED, mIsJobNamespaceMigrated); 2417 proto.write(SyncStatusProto.IS_JOB_ATTRIBUTION_FIXED, mIsJobAttributionFixed); 2418 2419 proto.flush(); 2420 } 2421 writeStatusStatsLocked(ProtoOutputStream proto, SyncStatusInfo.Stats stats)2422 private void writeStatusStatsLocked(ProtoOutputStream proto, SyncStatusInfo.Stats stats) { 2423 proto.write(SyncStatusProto.StatusInfo.Stats.TOTAL_ELAPSED_TIME, stats.totalElapsedTime); 2424 proto.write(SyncStatusProto.StatusInfo.Stats.NUM_SYNCS, stats.numSyncs); 2425 proto.write(SyncStatusProto.StatusInfo.Stats.NUM_FAILURES, stats.numFailures); 2426 proto.write(SyncStatusProto.StatusInfo.Stats.NUM_CANCELS, stats.numCancels); 2427 proto.write(SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_OTHER, stats.numSourceOther); 2428 proto.write(SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_LOCAL, stats.numSourceLocal); 2429 proto.write(SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_POLL, stats.numSourcePoll); 2430 proto.write(SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_USER, stats.numSourceUser); 2431 proto.write(SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_PERIODIC, stats.numSourcePeriodic); 2432 proto.write(SyncStatusProto.StatusInfo.Stats.NUM_SOURCE_FEED, stats.numSourceFeed); 2433 } 2434 requestSync(AuthorityInfo authorityInfo, int reason, Bundle extras, @SyncExemption int syncExemptionFlag, int callingUid, int callingPid)2435 private void requestSync(AuthorityInfo authorityInfo, int reason, Bundle extras, 2436 @SyncExemption int syncExemptionFlag, int callingUid, int callingPid) { 2437 if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID 2438 && mSyncRequestListener != null) { 2439 mSyncRequestListener.onSyncRequest(authorityInfo.target, reason, extras, 2440 syncExemptionFlag, callingUid, callingPid); 2441 } else { 2442 SyncRequest.Builder req = 2443 new SyncRequest.Builder() 2444 .syncOnce() 2445 .setExtras(extras); 2446 req.setSyncAdapter(authorityInfo.target.account, authorityInfo.target.provider); 2447 ContentResolver.requestSync(req.build()); 2448 } 2449 } 2450 requestSync(Account account, int userId, int reason, String authority, Bundle extras, @SyncExemption int syncExemptionFlag, int callingUid, int callingPid)2451 private void requestSync(Account account, int userId, int reason, String authority, 2452 Bundle extras, @SyncExemption int syncExemptionFlag, int callingUid, int callingPid) { 2453 // If this is happening in the system process, then call the syncrequest listener 2454 // to make a request back to the SyncManager directly. 2455 // If this is probably a test instance, then call back through the ContentResolver 2456 // which will know which userId to apply based on the Binder id. 2457 if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID 2458 && mSyncRequestListener != null) { 2459 mSyncRequestListener.onSyncRequest( 2460 new EndPoint(account, authority, userId), 2461 reason, extras, syncExemptionFlag, callingUid, callingPid); 2462 } else { 2463 ContentResolver.requestSync(account, authority, extras); 2464 } 2465 } 2466 2467 public static final int STATISTICS_FILE_END = 0; 2468 public static final int STATISTICS_FILE_ITEM_OLD = 100; 2469 public static final int STATISTICS_FILE_ITEM = 101; 2470 readStatsParcelLocked(File parcel)2471 private void readStatsParcelLocked(File parcel) { 2472 Parcel in = Parcel.obtain(); 2473 try { 2474 final AtomicFile parcelFile = new AtomicFile(parcel); 2475 byte[] data = parcelFile.readFully(); 2476 in.unmarshall(data, 0, data.length); 2477 in.setDataPosition(0); 2478 int token; 2479 int index = 0; 2480 while ((token=in.readInt()) != STATISTICS_FILE_END) { 2481 if (token == STATISTICS_FILE_ITEM || token == STATISTICS_FILE_ITEM_OLD) { 2482 int day = in.readInt(); 2483 if (token == STATISTICS_FILE_ITEM_OLD) { 2484 day = day - 2009 + 14245; // Magic! 2485 } 2486 DayStats ds = new DayStats(day); 2487 ds.successCount = in.readInt(); 2488 ds.successTime = in.readLong(); 2489 ds.failureCount = in.readInt(); 2490 ds.failureTime = in.readLong(); 2491 if (index < mDayStats.length) { 2492 mDayStats[index] = ds; 2493 index++; 2494 } 2495 } else { 2496 // Ooops. 2497 Slog.w(TAG, "Unknown stats token: " + token); 2498 break; 2499 } 2500 } 2501 } catch (IOException e) { 2502 Slog.i(TAG, "No initial statistics"); 2503 } finally { 2504 in.recycle(); 2505 } 2506 } 2507 upgradeStatisticsIfNeededLocked()2508 private void upgradeStatisticsIfNeededLocked() { 2509 final File parcelStats = new File(mSyncDir, LEGACY_STATISTICS_FILE_NAME); 2510 if (parcelStats.exists() && !mStatisticsFile.exists()) { 2511 readStatsParcelLocked(parcelStats); 2512 writeStatisticsLocked(); 2513 } 2514 2515 // if upgrade to proto was successful, delete parcel file 2516 if (DELETE_LEGACY_PARCEL_FILES && parcelStats.exists() && mStatisticsFile.exists()) { 2517 parcelStats.delete(); 2518 } 2519 } 2520 2521 /** 2522 * Read all sync statistics back in to the initial engine state. 2523 */ readStatisticsLocked()2524 private void readStatisticsLocked() { 2525 upgradeStatisticsIfNeededLocked(); 2526 2527 if (!mStatisticsFile.exists()) { 2528 return; 2529 } 2530 try { 2531 try (FileInputStream in = mStatisticsFile.openRead()) { 2532 readDayStatsLocked(in); 2533 } 2534 } catch (Exception e) { 2535 Slog.e(TAG, "Unable to read day stats file.", e); 2536 } 2537 } 2538 readDayStatsLocked(InputStream in)2539 private void readDayStatsLocked(InputStream in) throws IOException { 2540 final ProtoInputStream proto = new ProtoInputStream(in); 2541 int statsCount = 0; 2542 while (true) { 2543 switch (proto.nextField()) { 2544 case (int) SyncStatisticsProto.STATS: 2545 final long token = proto.start(SyncStatisticsProto.STATS); 2546 final DayStats stats = readIndividualDayStatsLocked(proto); 2547 proto.end(token); 2548 mDayStats[statsCount] = stats; 2549 statsCount++; 2550 if (statsCount == mDayStats.length) { 2551 return; 2552 } 2553 break; 2554 case ProtoInputStream.NO_MORE_FIELDS: 2555 return; 2556 } 2557 } 2558 } 2559 readIndividualDayStatsLocked(ProtoInputStream proto)2560 private DayStats readIndividualDayStatsLocked(ProtoInputStream proto) throws IOException { 2561 DayStats stats; 2562 if (proto.nextField(SyncStatisticsProto.DayStats.DAY)) { 2563 // fast-path; this should work for most cases since the day is written first 2564 stats = new DayStats(proto.readInt(SyncStatisticsProto.DayStats.DAY)); 2565 } else { 2566 // placeholder to read other data; assume the default day as 0 2567 stats = new DayStats(0); 2568 } 2569 2570 while (true) { 2571 switch (proto.nextField()) { 2572 case (int) SyncStatisticsProto.DayStats.DAY: 2573 // fast-path failed for some reason, rebuild stats from placeholder object 2574 Slog.w(TAG, "Failed to read the day via fast-path; some data " 2575 + "might not have been read."); 2576 final DayStats temp = new DayStats( 2577 proto.readInt(SyncStatisticsProto.DayStats.DAY)); 2578 temp.successCount = stats.successCount; 2579 temp.successTime = stats.successTime; 2580 temp.failureCount = stats.failureCount; 2581 temp.failureTime = stats.failureTime; 2582 stats = temp; 2583 break; 2584 case (int) SyncStatisticsProto.DayStats.SUCCESS_COUNT: 2585 stats.successCount = proto.readInt(SyncStatisticsProto.DayStats.SUCCESS_COUNT); 2586 break; 2587 case (int) SyncStatisticsProto.DayStats.SUCCESS_TIME: 2588 stats.successTime = proto.readLong(SyncStatisticsProto.DayStats.SUCCESS_TIME); 2589 break; 2590 case (int) SyncStatisticsProto.DayStats.FAILURE_COUNT: 2591 stats.failureCount = proto.readInt(SyncStatisticsProto.DayStats.FAILURE_COUNT); 2592 break; 2593 case (int) SyncStatisticsProto.DayStats.FAILURE_TIME: 2594 stats.failureTime = proto.readLong(SyncStatisticsProto.DayStats.FAILURE_TIME); 2595 break; 2596 case ProtoInputStream.NO_MORE_FIELDS: 2597 return stats; 2598 } 2599 } 2600 } 2601 2602 /** 2603 * Write all sync statistics to the sync status file. 2604 */ 2605 @VisibleForTesting writeStatisticsLocked()2606 void writeStatisticsLocked() { 2607 if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { 2608 Slog.v(TAG, "Writing new " + mStatisticsFile.getBaseFile()); 2609 } 2610 2611 // The file is being written, so we don't need to have a scheduled 2612 // write until the next change. 2613 mHandler.removeMessages(MSG_WRITE_STATISTICS); 2614 2615 FileOutputStream fos = null; 2616 try { 2617 fos = mStatisticsFile.startWrite(); 2618 writeDayStatsLocked(fos); 2619 mStatisticsFile.finishWrite(fos); 2620 fos = null; 2621 } catch (IOException | IllegalArgumentException e) { 2622 Slog.e(TAG, "Unable to write day stats to proto.", e); 2623 } finally { 2624 // when fos is null (successful write), this is a no-op. 2625 mStatisticsFile.failWrite(fos); 2626 } 2627 } 2628 writeDayStatsLocked(OutputStream out)2629 private void writeDayStatsLocked(OutputStream out) 2630 throws IOException, IllegalArgumentException { 2631 final ProtoOutputStream proto = new ProtoOutputStream(out); 2632 final int size = mDayStats.length; 2633 for (int i = 0; i < size; i++) { 2634 final DayStats stats = mDayStats[i]; 2635 if (stats == null) { 2636 break; 2637 } 2638 final long token = proto.start(SyncStatisticsProto.STATS); 2639 // day should be written first to take advantage of the fast path in read 2640 proto.write(SyncStatisticsProto.DayStats.DAY, stats.day); 2641 proto.write(SyncStatisticsProto.DayStats.SUCCESS_COUNT, stats.successCount); 2642 proto.write(SyncStatisticsProto.DayStats.SUCCESS_TIME, stats.successTime); 2643 proto.write(SyncStatisticsProto.DayStats.FAILURE_COUNT, stats.failureCount); 2644 proto.write(SyncStatisticsProto.DayStats.FAILURE_TIME, stats.failureTime); 2645 proto.end(token); 2646 } 2647 proto.flush(); 2648 } 2649 2650 /** 2651 * Let the BackupManager know that account sync settings have changed. This will trigger 2652 * {@link com.android.server.backup.SystemBackupAgent} to run. 2653 */ queueBackup()2654 public void queueBackup() { 2655 BackupManager.dataChanged("android"); 2656 } 2657 setClockValid()2658 public void setClockValid() { 2659 if (!mIsClockValid) { 2660 mIsClockValid = true; 2661 Slog.w(TAG, "Clock is valid now."); 2662 } 2663 } 2664 isClockValid()2665 public boolean isClockValid() { 2666 return mIsClockValid; 2667 } 2668 resetTodayStats(boolean force)2669 public void resetTodayStats(boolean force) { 2670 if (force) { 2671 Log.w(TAG, "Force resetting today stats."); 2672 } 2673 synchronized (mAuthorities) { 2674 final int N = mSyncStatus.size(); 2675 for (int i = 0; i < N; i++) { 2676 SyncStatusInfo cur = mSyncStatus.valueAt(i); 2677 cur.maybeResetTodayStats(isClockValid(), force); 2678 } 2679 writeStatusLocked(); 2680 } 2681 } 2682 } 2683