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