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