• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.content;
18 
19 import static android.os.PowerWhitelistManager.REASON_SYNC_MANAGER;
20 import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
21 
22 import static com.android.server.content.SyncLogger.logSafe;
23 
24 import android.accounts.Account;
25 import android.accounts.AccountAndUser;
26 import android.accounts.AccountManager;
27 import android.accounts.AccountManagerInternal;
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.annotation.UserIdInt;
31 import android.app.ActivityManagerInternal;
32 import android.app.AppGlobals;
33 import android.app.Notification;
34 import android.app.NotificationManager;
35 import android.app.PendingIntent;
36 import android.app.job.JobInfo;
37 import android.app.job.JobScheduler;
38 import android.app.usage.UsageStatsManagerInternal;
39 import android.content.BroadcastReceiver;
40 import android.content.ComponentName;
41 import android.content.ContentResolver;
42 import android.content.ContentResolver.SyncExemption;
43 import android.content.Context;
44 import android.content.ISyncAdapter;
45 import android.content.ISyncAdapterUnsyncableAccountCallback;
46 import android.content.ISyncContext;
47 import android.content.Intent;
48 import android.content.IntentFilter;
49 import android.content.PeriodicSync;
50 import android.content.ServiceConnection;
51 import android.content.SyncActivityTooManyDeletes;
52 import android.content.SyncAdapterType;
53 import android.content.SyncAdaptersCache;
54 import android.content.SyncInfo;
55 import android.content.SyncResult;
56 import android.content.SyncStatusInfo;
57 import android.content.SyncStatusInfo.Stats;
58 import android.content.pm.ApplicationInfo;
59 import android.content.pm.PackageInfo;
60 import android.content.pm.PackageManager;
61 import android.content.pm.PackageManager.NameNotFoundException;
62 import android.content.pm.PackageManagerInternal;
63 import android.content.pm.ProviderInfo;
64 import android.content.pm.RegisteredServicesCache;
65 import android.content.pm.RegisteredServicesCacheListener;
66 import android.content.pm.ResolveInfo;
67 import android.content.pm.UserInfo;
68 import android.database.ContentObserver;
69 import android.net.ConnectivityManager;
70 import android.net.NetworkInfo;
71 import android.net.TrafficStats;
72 import android.os.BatteryStats;
73 import android.os.Binder;
74 import android.os.Build;
75 import android.os.Bundle;
76 import android.os.Handler;
77 import android.os.HandlerThread;
78 import android.os.IBinder;
79 import android.os.Looper;
80 import android.os.Message;
81 import android.os.PowerManager;
82 import android.os.Process;
83 import android.os.RemoteCallback;
84 import android.os.RemoteException;
85 import android.os.ServiceManager;
86 import android.os.SystemClock;
87 import android.os.SystemProperties;
88 import android.os.UserHandle;
89 import android.os.UserManager;
90 import android.os.WorkSource;
91 import android.provider.Settings;
92 import android.text.TextUtils;
93 import android.text.format.TimeMigrationUtils;
94 import android.util.EventLog;
95 import android.util.Log;
96 import android.util.Pair;
97 import android.util.Slog;
98 import android.util.SparseBooleanArray;
99 
100 import com.android.internal.R;
101 import com.android.internal.annotations.GuardedBy;
102 import com.android.internal.app.IBatteryStats;
103 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
104 import com.android.internal.notification.SystemNotificationChannels;
105 import com.android.internal.os.BackgroundThread;
106 import com.android.internal.util.ArrayUtils;
107 import com.android.internal.util.IndentingPrintWriter;
108 import com.android.internal.util.function.QuadConsumer;
109 import com.android.server.DeviceIdleInternal;
110 import com.android.server.LocalServices;
111 import com.android.server.SystemService;
112 import com.android.server.accounts.AccountManagerService;
113 import com.android.server.backup.AccountSyncSettingsBackupHelper;
114 import com.android.server.content.SyncStorageEngine.AuthorityInfo;
115 import com.android.server.content.SyncStorageEngine.EndPoint;
116 import com.android.server.content.SyncStorageEngine.OnSyncRequestListener;
117 import com.android.server.job.JobSchedulerInternal;
118 
119 import dalvik.annotation.optimization.NeverCompile;
120 
121 import com.google.android.collect.Lists;
122 import com.google.android.collect.Maps;
123 
124 import java.io.FileDescriptor;
125 import java.io.PrintWriter;
126 import java.util.ArrayList;
127 import java.util.Arrays;
128 import java.util.Collection;
129 import java.util.Collections;
130 import java.util.Comparator;
131 import java.util.HashMap;
132 import java.util.HashSet;
133 import java.util.List;
134 import java.util.Map;
135 import java.util.Objects;
136 import java.util.Random;
137 import java.util.Set;
138 import java.util.function.Function;
139 import java.util.function.Predicate;
140 
141 /**
142  * Implementation details:
143  * All scheduled syncs will be passed on to JobScheduler as jobs
144  * (See {@link #scheduleSyncOperationH(SyncOperation, long)}. This function schedules a job
145  * with JobScheduler with appropriate delay and constraints (according to backoffs and extras).
146  * The scheduleSyncOperationH function also assigns a unique jobId to each
147  * SyncOperation.
148  *
149  * Periodic Syncs:
150  * Each periodic sync is scheduled as a periodic job. If a periodic sync fails, we create a new
151  * one off SyncOperation and set its {@link SyncOperation#sourcePeriodicId} field to the jobId of the
152  * periodic sync. We don't allow the periodic job to run while any job initiated by it is pending.
153  *
154  * Backoffs:
155  * Each {@link EndPoint} has a backoff associated with it. When a SyncOperation fails, we increase
156  * the backoff on the authority. Then we reschedule all syncs associated with that authority to
157  * run at a later time. Similarly, when a sync succeeds, backoff is cleared and all associated syncs
158  * are rescheduled. A rescheduled sync will get a new jobId.
159  *
160  * See also {@code SyncManager.md} in the same directory for how app-standby affects sync adapters.
161  *
162  * @hide
163  */
164 public class SyncManager {
165     static final String TAG = "SyncManager";
166 
167     private static final boolean DEBUG_ACCOUNT_ACCESS = false;
168 
169     // Only do the check on a debuggable build.
170     private static final boolean ENABLE_SUSPICIOUS_CHECK = Build.IS_DEBUGGABLE;
171 
172     /** Delay a sync due to local changes this long. In milliseconds */
173     private static final long LOCAL_SYNC_DELAY;
174 
175     static {
176         LOCAL_SYNC_DELAY =
177                 SystemProperties.getLong("sync.local_sync_delay", 30 * 1000 /* 30 seconds */);
178     }
179 
180     /**
181      * How long to wait before retrying a sync that failed due to one already being in progress.
182      */
183     private static final int DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS = 10;
184 
185     /**
186      * How often to periodically poll network traffic for an adapter performing a sync to determine
187      * whether progress is being made.
188      */
189     private static final long SYNC_MONITOR_WINDOW_LENGTH_MILLIS = 60 * 1000; // 60 seconds
190 
191     /**
192      * How many bytes must be transferred (Tx + Rx) over the period of time defined by
193      * {@link #SYNC_MONITOR_WINDOW_LENGTH_MILLIS} for the sync to be considered to be making
194      * progress.
195      */
196     private static final int SYNC_MONITOR_PROGRESS_THRESHOLD_BYTES = 10; // 10 bytes
197 
198     /**
199      * If a sync becomes ready and it conflicts with an already running sync, it gets
200      * pushed back for this amount of time.
201      */
202     private static final long SYNC_DELAY_ON_CONFLICT = 10*1000; // 10 seconds
203 
204     /**
205      * Generate job ids in the range [MIN_SYNC_JOB_ID, MAX_SYNC_JOB_ID) to avoid conflicts with
206      * other jobs scheduled by the system process.
207      */
208     private static final int MIN_SYNC_JOB_ID = 100000;
209     private static final int MAX_SYNC_JOB_ID = 110000;
210 
211     private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*/";
212     private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
213     private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
214 
215     private static final boolean USE_WTF_FOR_ACCOUNT_ERROR = true;
216 
217     private static final int SYNC_OP_STATE_VALID = 0;
218     // "1" used to include errors 3, 4 and 5 but now it's split up.
219     private static final int SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS = 2;
220     private static final int SYNC_OP_STATE_INVALID_NO_ACCOUNT = 3;
221     private static final int SYNC_OP_STATE_INVALID_NOT_SYNCABLE = 4;
222     private static final int SYNC_OP_STATE_INVALID_SYNC_DISABLED = 5;
223 
224     /** Flags used when connecting to a sync adapter service */
225     private static final int SYNC_ADAPTER_CONNECTION_FLAGS = Context.BIND_AUTO_CREATE
226             | Context.BIND_NOT_FOREGROUND | Context.BIND_ALLOW_OOM_MANAGEMENT;
227 
228     /** Singleton instance. */
229     @GuardedBy("SyncManager.class")
230     private static SyncManager sInstance;
231 
232     private Context mContext;
233 
234     private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0];
235 
236     private final Object mAccountsLock = new Object();
237     private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY;
238 
239     volatile private PowerManager.WakeLock mSyncManagerWakeLock;
240     volatile private boolean mDataConnectionIsConnected = false;
241     private volatile int mNextJobIdOffset = 0;
242 
243     private final NotificationManager mNotificationMgr;
244     private final IBatteryStats mBatteryStats;
245     private JobScheduler mJobScheduler;
246     private JobSchedulerInternal mJobSchedulerInternal;
247 
248     private SyncStorageEngine mSyncStorageEngine;
249 
250     protected final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList();
251 
252     // Synchronized on "this". Instead of using this directly one should instead call
253     // its accessor, getConnManager().
254     private ConnectivityManager mConnManagerDoNotUseDirectly;
255 
256     /** Track whether the device has already been provisioned. */
257     private volatile boolean mProvisioned;
258 
259     protected final SyncAdaptersCache mSyncAdapters;
260 
261     private final SyncLogger mLogger;
262 
isJobIdInUseLockedH(int jobId, List<JobInfo> pendingJobs)263     private boolean isJobIdInUseLockedH(int jobId, List<JobInfo> pendingJobs) {
264         for (int i = 0, size = pendingJobs.size(); i < size; i++) {
265             JobInfo job = pendingJobs.get(i);
266             if (job.getId() == jobId) {
267                 return true;
268             }
269         }
270         for (int i = 0, size = mActiveSyncContexts.size(); i < size; i++) {
271             ActiveSyncContext asc = mActiveSyncContexts.get(i);
272             if (asc.mSyncOperation.jobId == jobId) {
273                 return true;
274             }
275         }
276         return false;
277     }
278 
getUnusedJobIdH()279     private int getUnusedJobIdH() {
280         final int maxNumSyncJobIds = MAX_SYNC_JOB_ID - MIN_SYNC_JOB_ID;
281         final List<JobInfo> pendingJobs = mJobSchedulerInternal.getSystemScheduledPendingJobs();
282         for (int i = 0; i < maxNumSyncJobIds; ++i) {
283             int newJobId = MIN_SYNC_JOB_ID + ((mNextJobIdOffset + i) % maxNumSyncJobIds);
284             if (!isJobIdInUseLockedH(newJobId, pendingJobs)) {
285                 mNextJobIdOffset = (mNextJobIdOffset + i + 1) % maxNumSyncJobIds;
286                 return newJobId;
287             }
288         }
289         // We've used all 10,000 intended job IDs.... We're probably in a world of pain right now :/
290         Slog.wtf(TAG, "All " + maxNumSyncJobIds + " possible sync job IDs are taken :/");
291         mNextJobIdOffset = (mNextJobIdOffset + 1) % maxNumSyncJobIds;
292         return MIN_SYNC_JOB_ID + mNextJobIdOffset;
293     }
294 
getAllPendingSyncs()295     private List<SyncOperation> getAllPendingSyncs() {
296         verifyJobScheduler();
297         List<JobInfo> pendingJobs = mJobSchedulerInternal.getSystemScheduledPendingJobs();
298         final int numJobs = pendingJobs.size();
299         final List<SyncOperation> pendingSyncs = new ArrayList<>(numJobs);
300         for (int i = 0; i < numJobs; ++i) {
301             final JobInfo job = pendingJobs.get(i);
302             SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras());
303             if (op != null) {
304                 pendingSyncs.add(op);
305             }
306         }
307         return pendingSyncs;
308     }
309 
310     private final BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
311         @Override
312         public void onReceive(Context context, Intent intent) {
313             EndPoint target = new EndPoint(null, null, getSendingUserId());
314             updateRunningAccounts(target /* sync targets for user */);
315         }
316     };
317 
318     private final PowerManager mPowerManager;
319 
320     private final UserManager mUserManager;
321 
322     private final AccountManager mAccountManager;
323 
324     private final AccountManagerInternal mAccountManagerInternal;
325 
326     private final PackageManagerInternal mPackageManagerInternal;
327 
328     private final ActivityManagerInternal mAmi;
329 
getAllUsers()330     private List<UserInfo> getAllUsers() {
331         return mUserManager.getUsers();
332     }
333 
containsAccountAndUser(AccountAndUser[] accounts, Account account, int userId)334     private boolean containsAccountAndUser(AccountAndUser[] accounts, Account account, int userId) {
335         boolean found = false;
336         for (int i = 0; i < accounts.length; i++) {
337             if (accounts[i].userId == userId
338                     && accounts[i].account.equals(account)) {
339                 found = true;
340                 break;
341             }
342         }
343         return found;
344     }
345 
346     /** target indicates endpoints that should be synced after account info is updated. */
updateRunningAccounts(EndPoint target)347     private void updateRunningAccounts(EndPoint target) {
348         if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_ACCOUNTS_UPDATED");
349         // Update accounts in handler thread.
350         Message m = mSyncHandler.obtainMessage(SyncHandler.MESSAGE_ACCOUNTS_UPDATED);
351         m.obj = target;
352         m.sendToTarget();
353     }
354 
removeStaleAccounts()355     private void removeStaleAccounts() {
356         for (UserInfo user : mUserManager.getAliveUsers()) {
357             // Skip any partially created/removed users
358             if (user.partial) continue;
359             Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(
360                     user.id, mContext.getOpPackageName());
361 
362             mSyncStorageEngine.removeStaleAccounts(accountsForUser, user.id);
363         }
364     }
365 
366     private BroadcastReceiver mConnectivityIntentReceiver =
367             new BroadcastReceiver() {
368                 @Override
369                 public void onReceive(Context context, Intent intent) {
370                     final boolean wasConnected = mDataConnectionIsConnected;
371 
372                     // Don't use the intent to figure out if network is connected, just check
373                     // ConnectivityManager directly.
374                     mDataConnectionIsConnected = readDataConnectionState();
375                     if (mDataConnectionIsConnected) {
376                         if (!wasConnected) {
377                             if (Log.isLoggable(TAG, Log.VERBOSE)) {
378                                 Slog.v(TAG, "Reconnection detected: clearing all backoffs");
379                             }
380                             // Note the location of this code was wrong from nyc to oc; fixed in DR.
381                             clearAllBackoffs("network reconnect");
382                         }
383                     }
384                 }
385             };
386 
clearAllBackoffs(String why)387     private void clearAllBackoffs(String why) {
388         mSyncStorageEngine.clearAllBackoffsLocked();
389         rescheduleSyncs(EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL, why);
390     }
391 
readDataConnectionState()392     private boolean readDataConnectionState() {
393         NetworkInfo networkInfo = getConnectivityManager().getActiveNetworkInfo();
394         return (networkInfo != null) && networkInfo.isConnected();
395     }
396 
getJobStats()397     private String getJobStats() {
398         JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
399         return "JobStats: "
400                 + ((js == null) ? "(JobSchedulerInternal==null)"
401                 : js.getPersistStats().toString());
402     }
403 
404     private BroadcastReceiver mShutdownIntentReceiver =
405             new BroadcastReceiver() {
406                 @Override
407                 public void onReceive(Context context, Intent intent) {
408                     Log.w(TAG, "Writing sync state before shutdown...");
409                     getSyncStorageEngine().writeAllState();
410 
411                     mLogger.log(getJobStats());
412                     mLogger.log("Shutting down.");
413                 }
414             };
415 
416     private final BroadcastReceiver mOtherIntentsReceiver =
417             new BroadcastReceiver() {
418                 @Override
419                 public void onReceive(Context context, Intent intent) {
420                     if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
421                         mSyncStorageEngine.setClockValid();
422                         return;
423                     }
424                 }
425             };
426 
427     private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
428         @Override
429         public void onReceive(Context context, Intent intent) {
430             String action = intent.getAction();
431             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
432             if (userId == UserHandle.USER_NULL) return;
433 
434             if (Intent.ACTION_USER_REMOVED.equals(action)) {
435                 onUserRemoved(userId);
436             } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
437                 onUserUnlocked(userId);
438             } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
439                 onUserStopped(userId);
440             }
441         }
442     };
443 
444     private final HandlerThread mThread;
445     private final SyncHandler mSyncHandler;
446     private final SyncManagerConstants mConstants;
447 
448     @GuardedBy("mUnlockedUsers")
449     private final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray();
450 
getConnectivityManager()451     private ConnectivityManager getConnectivityManager() {
452         synchronized (this) {
453             if (mConnManagerDoNotUseDirectly == null) {
454                 mConnManagerDoNotUseDirectly = (ConnectivityManager)mContext.getSystemService(
455                         Context.CONNECTIVITY_SERVICE);
456             }
457             return mConnManagerDoNotUseDirectly;
458         }
459     }
460 
461     /**
462      * Cancel all unnecessary jobs. This function will be run once after every boot.
463      */
cleanupJobs()464     private void cleanupJobs() {
465         // O(n^2) in number of jobs, so we run this on the background thread.
466         mSyncHandler.postAtFrontOfQueue(new Runnable() {
467             @Override
468             public void run() {
469                 List<SyncOperation> ops = getAllPendingSyncs();
470                 Set<String> cleanedKeys = new HashSet<String>();
471                 for (SyncOperation opx: ops) {
472                     if (cleanedKeys.contains(opx.key)) {
473                         continue;
474                     }
475                     cleanedKeys.add(opx.key);
476                     for (SyncOperation opy: ops) {
477                         if (opx == opy) {
478                             continue;
479                         }
480                         if (opx.key.equals(opy.key)) {
481                             mLogger.log("Removing duplicate sync: ", opy);
482                             cancelJob(opy, "cleanupJobs() x=" + opx + " y=" + opy);
483                         }
484                     }
485                 }
486             }
487         });
488     }
489 
verifyJobScheduler()490     private synchronized void verifyJobScheduler() {
491         if (mJobScheduler != null) {
492             return;
493         }
494         final long token = Binder.clearCallingIdentity();
495         try {
496             if (Log.isLoggable(TAG, Log.VERBOSE)) {
497                 Log.d(TAG, "initializing JobScheduler object.");
498             }
499             mJobScheduler = (JobScheduler) mContext.getSystemService(
500                     Context.JOB_SCHEDULER_SERVICE);
501             mJobSchedulerInternal = LocalServices.getService(JobSchedulerInternal.class);
502             // Get all persisted syncs from JobScheduler
503             List<JobInfo> pendingJobs = mJobScheduler.getAllPendingJobs();
504 
505             int numPersistedPeriodicSyncs = 0;
506             int numPersistedOneshotSyncs = 0;
507             for (JobInfo job : pendingJobs) {
508                 SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras());
509                 if (op != null) {
510                     if (op.isPeriodic) {
511                         numPersistedPeriodicSyncs++;
512                     } else {
513                         numPersistedOneshotSyncs++;
514                         // Set the pending status of this EndPoint to true. Pending icon is
515                         // shown on the settings activity.
516                         mSyncStorageEngine.markPending(op.target, true);
517                     }
518                 }
519             }
520             final String summary = "Loaded persisted syncs: "
521                     + numPersistedPeriodicSyncs + " periodic syncs, "
522                     + numPersistedOneshotSyncs + " oneshot syncs, "
523                     + (pendingJobs.size()) + " total system server jobs, "
524                     + getJobStats();
525             Slog.i(TAG, summary);
526             mLogger.log(summary);
527 
528             cleanupJobs();
529 
530             if (ENABLE_SUSPICIOUS_CHECK &&
531                     (numPersistedPeriodicSyncs == 0) && likelyHasPeriodicSyncs()) {
532                 Slog.wtf(TAG, "Device booted with no persisted periodic syncs: " + summary);
533             }
534         } finally {
535             Binder.restoreCallingIdentity(token);
536         }
537     }
538 
539     /**
540      * @return whether the device most likely has some periodic syncs.
541      */
likelyHasPeriodicSyncs()542     private boolean likelyHasPeriodicSyncs() {
543         try {
544             // Each sync adapter has a daily periodic sync by default, but sync adapters can remove
545             // them by themselves. So here, we use an arbitrary threshold. If there are more than
546             // this many sync endpoints, surely one of them should have a periodic sync...
547             return mSyncStorageEngine.getAuthorityCount() >= 6;
548         } catch (Throwable th) {
549             // Just in case.
550         }
551         return false;
552     }
553 
getJobScheduler()554     private JobScheduler getJobScheduler() {
555         verifyJobScheduler();
556         return mJobScheduler;
557     }
558 
SyncManager(Context context, boolean factoryTest)559     public SyncManager(Context context, boolean factoryTest) {
560         synchronized (SyncManager.class) {
561             if (sInstance == null) {
562                 sInstance = this;
563             } else {
564                 Slog.wtf(TAG, "SyncManager instantiated multiple times");
565             }
566         }
567 
568         // Initialize the SyncStorageEngine first, before registering observers
569         // and creating threads and so on; it may fail if the disk is full.
570         mContext = context;
571 
572         mLogger = SyncLogger.getInstance();
573 
574         SyncStorageEngine.init(context, BackgroundThread.get().getLooper());
575         mSyncStorageEngine = SyncStorageEngine.getSingleton();
576         mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() {
577             @Override
578             public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras,
579                     @SyncExemption int syncExemptionFlag, int callingUid, int callingPid) {
580                 scheduleSync(info.account, info.userId, reason, info.provider, extras,
581                         AuthorityInfo.UNDEFINED, syncExemptionFlag, callingUid, callingPid, null);
582             }
583         });
584 
585         mSyncStorageEngine.setPeriodicSyncAddedListener(
586                 new SyncStorageEngine.PeriodicSyncAddedListener() {
587                     @Override
588                     public void onPeriodicSyncAdded(EndPoint target, Bundle extras, long pollFrequency,
589                             long flex) {
590                         updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
591                     }
592                 });
593 
594         mSyncStorageEngine.setOnAuthorityRemovedListener(new SyncStorageEngine.OnAuthorityRemovedListener() {
595             @Override
596             public void onAuthorityRemoved(EndPoint removedAuthority) {
597                 removeSyncsForAuthority(removedAuthority, "onAuthorityRemoved");
598             }
599         });
600 
601         mSyncAdapters = new SyncAdaptersCache(mContext);
602 
603         mThread = new HandlerThread("SyncManager", android.os.Process.THREAD_PRIORITY_BACKGROUND);
604         mThread.start();
605         mSyncHandler = new SyncHandler(mThread.getLooper());
606 
607         mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() {
608             @Override
609             public void onServiceChanged(SyncAdapterType type, int userId, boolean removed) {
610                 if (!removed) {
611                     scheduleSync(null, UserHandle.USER_ALL,
612                             SyncOperation.REASON_SERVICE_CHANGED,
613                             type.authority, null, AuthorityInfo.UNDEFINED,
614                             ContentResolver.SYNC_EXEMPTION_NONE,
615                             Process.myUid(), -1, null);
616                 }
617             }
618         }, mSyncHandler);
619 
620         mConstants = new SyncManagerConstants(context);
621 
622         IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
623         context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
624 
625         intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
626         intentFilter.setPriority(100);
627         context.registerReceiver(mShutdownIntentReceiver, intentFilter);
628 
629         intentFilter = new IntentFilter();
630         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
631         intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
632         intentFilter.addAction(Intent.ACTION_USER_STOPPED);
633         mContext.registerReceiverAsUser(
634                 mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
635 
636         intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
637         context.registerReceiver(mOtherIntentsReceiver, intentFilter);
638 
639         if (!factoryTest) {
640             mNotificationMgr = (NotificationManager)
641                     context.getSystemService(Context.NOTIFICATION_SERVICE);
642         } else {
643             mNotificationMgr = null;
644         }
645         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
646         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
647         mAccountManager = (AccountManager) mContext.getSystemService(Context.ACCOUNT_SERVICE);
648         mAccountManagerInternal = LocalServices.getService(AccountManagerInternal.class);
649         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
650         mAmi = LocalServices.getService(ActivityManagerInternal.class);
651 
652         mAccountManagerInternal.addOnAppPermissionChangeListener((Account account, int uid) -> {
653             // If the UID gained access to the account kick-off syncs lacking account access
654             if (mAccountManagerInternal.hasAccountAccess(account, uid)) {
655                 scheduleSync(account, UserHandle.getUserId(uid),
656                         SyncOperation.REASON_ACCOUNTS_UPDATED,
657                         null, null, AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS,
658                         ContentResolver.SYNC_EXEMPTION_NONE,
659                         Process.myUid(), -2, null);
660             }
661         });
662 
663         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
664                 BatteryStats.SERVICE_NAME));
665 
666         // This WakeLock is used to ensure that we stay awake while running the sync loop
667         // message handler. Normally we will hold a sync adapter wake lock while it is being
668         // synced but during the execution of the sync loop it might finish a sync for
669         // one sync adapter before starting the sync for the other sync adapter and we
670         // don't want the device to go to sleep during that window.
671         mSyncManagerWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
672                 SYNC_LOOP_WAKE_LOCK);
673         mSyncManagerWakeLock.setReferenceCounted(false);
674 
675         mProvisioned = isDeviceProvisioned();
676         if (!mProvisioned) {
677             final ContentResolver resolver = context.getContentResolver();
678             ContentObserver provisionedObserver =
679                     new ContentObserver(null /* current thread */) {
680                         public void onChange(boolean selfChange) {
681                             mProvisioned |= isDeviceProvisioned();
682                             if (mProvisioned) {
683                                 resolver.unregisterContentObserver(this);
684                             }
685                         }
686                     };
687 
688             synchronized (mSyncHandler) {
689                 resolver.registerContentObserver(
690                         Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
691                         false /* notifyForDescendents */,
692                         provisionedObserver);
693 
694                 // The device *may* have been provisioned while we were registering above observer.
695                 // Check again to make sure.
696                 mProvisioned |= isDeviceProvisioned();
697                 if (mProvisioned) {
698                     resolver.unregisterContentObserver(provisionedObserver);
699                 }
700             }
701         }
702 
703         if (!factoryTest) {
704             // Register for account list updates for all users
705             mContext.registerReceiverAsUser(mAccountsUpdatedReceiver,
706                     UserHandle.ALL,
707                     new IntentFilter(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION),
708                     null, null);
709         }
710 
711         // Sync adapters were able to access the synced account without the accounts
712         // permission which circumvents our permission model. Therefore, we require
713         // sync adapters that don't have access to the account to get user consent.
714         // This can be noisy, therefore we will allowlist sync adapters installed
715         // before we started checking for account access because they already know
716         // the account (they run before) which is the genie is out of the bottle.
717         whiteListExistingSyncAdaptersIfNeeded();
718 
719         mLogger.log("Sync manager initialized: " + Build.FINGERPRINT);
720     }
721 
onStartUser(int userId)722     public void onStartUser(int userId) {
723         // Log on the handler to avoid slowing down device boot.
724         mSyncHandler.post(() -> mLogger.log("onStartUser: user=", userId));
725     }
726 
onUnlockUser(int userId)727     public void onUnlockUser(int userId) {
728         synchronized (mUnlockedUsers) {
729             mUnlockedUsers.put(userId, true);
730         }
731         // Log on the handler to avoid slowing down device boot.
732         mSyncHandler.post(() -> mLogger.log("onUnlockUser: user=", userId));
733     }
734 
onStopUser(int userId)735     public void onStopUser(int userId) {
736         synchronized (mUnlockedUsers) {
737             mUnlockedUsers.put(userId, false);
738         }
739         // Log on the handler to avoid slowing down user switch.
740         mSyncHandler.post(() -> mLogger.log("onStopUser: user=", userId));
741     }
742 
isUserUnlocked(int userId)743     private boolean isUserUnlocked(int userId) {
744         synchronized (mUnlockedUsers) {
745             return mUnlockedUsers.get(userId);
746         }
747     }
748 
onBootPhase(int phase)749     public void onBootPhase(int phase) {
750         // Note SyncManager only receives PHASE_ACTIVITY_MANAGER_READY and after.
751         switch (phase) {
752             case SystemService.PHASE_ACTIVITY_MANAGER_READY:
753                 mConstants.start();
754                 break;
755         }
756     }
757 
whiteListExistingSyncAdaptersIfNeeded()758     private void whiteListExistingSyncAdaptersIfNeeded() {
759         if (!mSyncStorageEngine.shouldGrantSyncAdaptersAccountAccess()) {
760             return;
761         }
762         List<UserInfo> users = mUserManager.getAliveUsers();
763         final int userCount = users.size();
764         for (int i = 0; i < userCount; i++) {
765             UserHandle userHandle = users.get(i).getUserHandle();
766             final int userId = userHandle.getIdentifier();
767             for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> service
768                     : mSyncAdapters.getAllServices(userId)) {
769                 String packageName = service.componentName.getPackageName();
770                 for (Account account : mAccountManager.getAccountsByTypeAsUser(
771                         service.type.accountType, userHandle)) {
772                     if (!canAccessAccount(account, packageName, userId)) {
773                         mAccountManager.updateAppPermission(account,
774                                 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, service.uid, true);
775                     }
776                 }
777             }
778         }
779     }
780 
isDeviceProvisioned()781     private boolean isDeviceProvisioned() {
782         final ContentResolver resolver = mContext.getContentResolver();
783         return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
784     }
785     /**
786      * Return a random value v that satisfies minValue <= v < maxValue. The difference between
787      * maxValue and minValue must be less than Integer.MAX_VALUE.
788      */
jitterize(long minValue, long maxValue)789     private long jitterize(long minValue, long maxValue) {
790         Random random = new Random(SystemClock.elapsedRealtime());
791         long spread = maxValue - minValue;
792         if (spread > Integer.MAX_VALUE) {
793             throw new IllegalArgumentException("the difference between the maxValue and the "
794                     + "minValue must be less than " + Integer.MAX_VALUE);
795         }
796         return minValue + random.nextInt((int)spread);
797     }
798 
getSyncStorageEngine()799     public SyncStorageEngine getSyncStorageEngine() {
800         return mSyncStorageEngine;
801     }
802 
getIsSyncable(Account account, int userId, String providerName)803     private int getIsSyncable(Account account, int userId, String providerName) {
804         int isSyncable = mSyncStorageEngine.getIsSyncable(account, userId, providerName);
805         UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
806 
807         // If it's not a restricted user, return isSyncable.
808         if (userInfo == null || !userInfo.isRestricted()) return isSyncable;
809 
810         // Else check if the sync adapter has opted-in or not.
811         RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
812                 mSyncAdapters.getServiceInfo(
813                         SyncAdapterType.newKey(providerName, account.type), userId);
814         if (syncAdapterInfo == null) return AuthorityInfo.NOT_SYNCABLE;
815 
816         PackageInfo pInfo = null;
817         try {
818             pInfo = AppGlobals.getPackageManager().getPackageInfo(
819                     syncAdapterInfo.componentName.getPackageName(), 0, userId);
820             if (pInfo == null) return AuthorityInfo.NOT_SYNCABLE;
821         } catch (RemoteException re) {
822             // Shouldn't happen.
823             return AuthorityInfo.NOT_SYNCABLE;
824         }
825         if (pInfo.restrictedAccountType != null
826                 && pInfo.restrictedAccountType.equals(account.type)) {
827             return isSyncable;
828         } else {
829             return AuthorityInfo.NOT_SYNCABLE;
830         }
831     }
832 
setAuthorityPendingState(EndPoint info)833     private void setAuthorityPendingState(EndPoint info) {
834         List<SyncOperation> ops = getAllPendingSyncs();
835         for (SyncOperation op: ops) {
836             if (!op.isPeriodic && op.target.matchesSpec(info)) {
837                 getSyncStorageEngine().markPending(info, true);
838                 return;
839             }
840         }
841         getSyncStorageEngine().markPending(info, false);
842     }
843 
844     /**
845      * Initiate a sync. This can start a sync for all providers
846      * (pass null to url, set onlyTicklable to false), only those
847      * providers that are marked as ticklable (pass null to url,
848      * set onlyTicklable to true), or a specific provider (set url
849      * to the content url of the provider).
850      *
851      * <p>If the ContentResolver.SYNC_EXTRAS_UPLOAD boolean in extras is
852      * true then initiate a sync that just checks for local changes to send
853      * to the server, otherwise initiate a sync that first gets any
854      * changes from the server before sending local changes back to
855      * the server.
856      *
857      * <p>If a specific provider is being synced (the url is non-null)
858      * then the extras can contain SyncAdapter-specific information
859      * to control what gets synced (e.g. which specific feed to sync).
860      *
861      * <p>You'll start getting callbacks after this.
862      *
863      * @param requestedAccount the account to sync, may be null to signify all accounts
864      * @param userId the id of the user whose accounts are to be synced. If userId is USER_ALL,
865      *          then all users' accounts are considered.
866      * @param reason for sync request. If this is a positive integer, it is the Linux uid
867      * assigned to the process that requested the sync. If it's negative, the sync was requested by
868      * the SyncManager itself and could be one of the following:
869      *      {@link SyncOperation#REASON_BACKGROUND_DATA_SETTINGS_CHANGED}
870      *      {@link SyncOperation#REASON_ACCOUNTS_UPDATED}
871      *      {@link SyncOperation#REASON_SERVICE_CHANGED}
872      *      {@link SyncOperation#REASON_PERIODIC}
873      *      {@link SyncOperation#REASON_IS_SYNCABLE}
874      *      {@link SyncOperation#REASON_SYNC_AUTO}
875      *      {@link SyncOperation#REASON_MASTER_SYNC_AUTO}
876      *      {@link SyncOperation#REASON_USER_START}
877      * @param requestedAuthority the authority to sync, may be null to indicate all authorities
878      * @param extras a Map of SyncAdapter-specific information to control
879      *          syncs of a specific provider. Can be null. Is ignored
880      *          if the url is null.
881      * @param targetSyncState Only sync authorities that have the specified sync state.
882      *           Use {@link AuthorityInfo#UNDEFINED} to sync all authorities.
883      */
scheduleSync(Account requestedAccount, int userId, int reason, String requestedAuthority, Bundle extras, int targetSyncState, @SyncExemption int syncExemptionFlag, int callingUid, int callingPid, String callingPackage)884     public void scheduleSync(Account requestedAccount, int userId, int reason,
885             String requestedAuthority, Bundle extras, int targetSyncState,
886             @SyncExemption int syncExemptionFlag, int callingUid, int callingPid,
887             String callingPackage) {
888         scheduleSync(requestedAccount, userId, reason, requestedAuthority, extras, targetSyncState,
889                 0 /* min delay */, true /* checkIfAccountReady */, syncExemptionFlag,
890                 callingUid, callingPid, callingPackage);
891     }
892 
893     /**
894      * @param minDelayMillis The sync can't land before this delay expires.
895      */
scheduleSync(Account requestedAccount, int userId, int reason, String requestedAuthority, Bundle extras, int targetSyncState, final long minDelayMillis, boolean checkIfAccountReady, @SyncExemption int syncExemptionFlag, int callingUid, int callingPid, String callingPackage)896     private void scheduleSync(Account requestedAccount, int userId, int reason,
897             String requestedAuthority, Bundle extras, int targetSyncState,
898             final long minDelayMillis, boolean checkIfAccountReady,
899             @SyncExemption int syncExemptionFlag,
900             int callingUid, int callingPid, String callingPackage) {
901         if (extras == null) {
902             extras = new Bundle();
903         }
904         extras.size(); // Force unpacel.
905         if (Log.isLoggable(TAG, Log.VERBOSE)) {
906             mLogger.log("scheduleSync: account=", requestedAccount,
907                     " u", userId,
908                     " authority=", requestedAuthority,
909                     " reason=", reason,
910                     " extras=", extras,
911                     " cuid=", callingUid, " cpid=", callingPid, " cpkg=", callingPackage,
912                     " mdm=", minDelayMillis,
913                     " ciar=", checkIfAccountReady,
914                     " sef=", syncExemptionFlag);
915         }
916 
917         AccountAndUser[] accounts = null;
918         synchronized (mAccountsLock) {
919             if (requestedAccount != null) {
920                 if (userId != UserHandle.USER_ALL) {
921                     accounts = new AccountAndUser[]{new AccountAndUser(requestedAccount, userId)};
922                 } else {
923                     for (AccountAndUser runningAccount : mRunningAccounts) {
924                         if (requestedAccount.equals(runningAccount.account)) {
925                             accounts = ArrayUtils.appendElement(AccountAndUser.class,
926                                     accounts, runningAccount);
927                         }
928                     }
929                 }
930             } else {
931                 accounts = mRunningAccounts;
932             }
933         }
934 
935         if (ArrayUtils.isEmpty(accounts)) {
936             return;
937         }
938 
939         final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false);
940         final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
941         if (manualSync) {
942             extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true);
943             extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
944         }
945         final boolean ignoreSettings =
946                 extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
947 
948         int source;
949         if (uploadOnly) {
950             source = SyncStorageEngine.SOURCE_LOCAL;
951         } else if (manualSync) {
952             source = SyncStorageEngine.SOURCE_USER;
953         } else if (requestedAuthority == null) {
954             source = SyncStorageEngine.SOURCE_POLL;
955         } else {
956             if (extras.containsKey("feed")) {
957                 source = SyncStorageEngine.SOURCE_FEED;
958             } else{
959                 // This isn't strictly server, since arbitrary callers can (and do) request
960                 // a non-forced two-way sync on a specific url.
961                 source = SyncStorageEngine.SOURCE_OTHER;
962             }
963         }
964 
965         for (AccountAndUser account : accounts) {
966             // If userId is specified, do not sync accounts of other users
967             if (userId >= UserHandle.USER_SYSTEM && account.userId >= UserHandle.USER_SYSTEM
968                     && userId != account.userId) {
969                 continue;
970             }
971             // Compile a list of authorities that have sync adapters.
972             // For each authority sync each account that matches a sync adapter.
973             final HashSet<String> syncableAuthorities = new HashSet<String>();
974             for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter :
975                     mSyncAdapters.getAllServices(account.userId)) {
976                 syncableAuthorities.add(syncAdapter.type.authority);
977             }
978 
979             // If the url was specified then replace the list of authorities
980             // with just this authority or clear it if this authority isn't
981             // syncable.
982             if (requestedAuthority != null) {
983                 final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority);
984                 syncableAuthorities.clear();
985                 if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority);
986             }
987 
988             for (String authority : syncableAuthorities) {
989                 int isSyncable = computeSyncable(account.account, account.userId, authority,
990                         !checkIfAccountReady);
991 
992                 if (isSyncable == AuthorityInfo.NOT_SYNCABLE) {
993                     continue;
994                 }
995 
996                 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
997                         mSyncAdapters.getServiceInfo(SyncAdapterType.newKey(authority,
998                                 account.account.type), account.userId);
999                 if (syncAdapterInfo == null) {
1000                     continue;
1001                 }
1002 
1003                 final int owningUid = syncAdapterInfo.uid;
1004 
1005                 if (isSyncable == AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS) {
1006                     mLogger.log("scheduleSync: Not scheduling sync operation: "
1007                                 + "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS");
1008                     Bundle finalExtras = new Bundle(extras);
1009                     String packageName = syncAdapterInfo.componentName.getPackageName();
1010                     // If the app did not run and has no account access, done
1011                     if (!wasPackageEverLaunched(packageName, userId)) {
1012                         continue;
1013                     }
1014                     mAccountManagerInternal.requestAccountAccess(account.account,
1015                             packageName, userId,
1016                             new RemoteCallback((Bundle result) -> {
1017                                 if (result != null
1018                                         && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
1019                                     scheduleSync(account.account, userId, reason, authority,
1020                                             finalExtras, targetSyncState, minDelayMillis,
1021                                             true /* checkIfAccountReady */,
1022                                             syncExemptionFlag, callingUid, callingPid,
1023                                             callingPackage);
1024                                 }
1025                             }
1026                         ));
1027                     continue;
1028                 }
1029 
1030                 final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs();
1031                 final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable();
1032                 if (!checkIfAccountReady && isSyncable < 0 && isAlwaysSyncable) {
1033                     mSyncStorageEngine.setIsSyncable(
1034                             account.account, account.userId, authority, AuthorityInfo.SYNCABLE,
1035                             callingUid, callingPid);
1036                     isSyncable = AuthorityInfo.SYNCABLE;
1037                 }
1038 
1039                 if (targetSyncState != AuthorityInfo.UNDEFINED && targetSyncState != isSyncable) {
1040                     continue;
1041                 }
1042 
1043                 if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) {
1044                     continue;
1045                 }
1046 
1047                 boolean syncAllowed =
1048                         (isSyncable < 0) // Always allow if the isSyncable state is unknown.
1049                                 || ignoreSettings
1050                                 || (mSyncStorageEngine.getMasterSyncAutomatically(account.userId)
1051                                 && mSyncStorageEngine.getSyncAutomatically(account.account,
1052                                 account.userId, authority));
1053                 if (!syncAllowed) {
1054                     mLogger.log("scheduleSync: sync of ", account, " ", authority,
1055                             " is not allowed, dropping request");
1056                     continue;
1057                 }
1058                 SyncStorageEngine.EndPoint info =
1059                         new SyncStorageEngine.EndPoint(
1060                                 account.account, authority, account.userId);
1061                 long delayUntil =
1062                         mSyncStorageEngine.getDelayUntilTime(info);
1063 
1064                 final String owningPackage = syncAdapterInfo.componentName.getPackageName();
1065 
1066                 if (isSyncable == AuthorityInfo.NOT_INITIALIZED) {
1067                     if (checkIfAccountReady) {
1068                         Bundle finalExtras = new Bundle(extras);
1069 
1070                         sendOnUnsyncableAccount(mContext, syncAdapterInfo, account.userId,
1071                                 () -> scheduleSync(account.account, account.userId, reason,
1072                                         authority, finalExtras, targetSyncState, minDelayMillis,
1073                                         false, syncExemptionFlag, callingUid, callingPid,
1074                                         callingPackage));
1075                     } else {
1076                         // Initialisation sync.
1077                         Bundle newExtras = new Bundle();
1078                         newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
1079 
1080                         mLogger.log("scheduleSync: schedule initialisation sync ",
1081                                 account, " ", authority);
1082 
1083                         postScheduleSyncMessage(
1084                                 new SyncOperation(account.account, account.userId,
1085                                         owningUid, owningPackage, reason, source,
1086                                         authority, newExtras, allowParallelSyncs,
1087                                         syncExemptionFlag),
1088                                 minDelayMillis
1089                         );
1090                     }
1091                 } else if (targetSyncState == AuthorityInfo.UNDEFINED
1092                         || targetSyncState == isSyncable) {
1093                     mLogger.log("scheduleSync: scheduling sync ",
1094                             account, " ", authority);
1095                     postScheduleSyncMessage(
1096                             new SyncOperation(account.account, account.userId,
1097                                     owningUid, owningPackage, reason, source,
1098                                     authority, extras, allowParallelSyncs, syncExemptionFlag),
1099                             minDelayMillis
1100                     );
1101                 } else {
1102                     mLogger.log("scheduleSync: not handling ",
1103                             account, " ", authority);
1104                 }
1105             }
1106         }
1107     }
1108 
computeSyncable(Account account, int userId, String authority, boolean checkAccountAccess)1109     public int computeSyncable(Account account, int userId, String authority,
1110             boolean checkAccountAccess) {
1111         final int status = getIsSyncable(account, userId, authority);
1112         if (status == AuthorityInfo.NOT_SYNCABLE) {
1113             return AuthorityInfo.NOT_SYNCABLE;
1114         }
1115         final SyncAdapterType type = SyncAdapterType.newKey(authority, account.type);
1116         final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
1117                 mSyncAdapters.getServiceInfo(type, userId);
1118         if (syncAdapterInfo == null) {
1119             return AuthorityInfo.NOT_SYNCABLE;
1120         }
1121         final int owningUid = syncAdapterInfo.uid;
1122         final String owningPackage = syncAdapterInfo.componentName.getPackageName();
1123         if (mAmi.isAppStartModeDisabled(owningUid, owningPackage)) {
1124             Slog.w(TAG, "Not scheduling job " + syncAdapterInfo.uid + ":"
1125                     + syncAdapterInfo.componentName
1126                     + " -- package not allowed to start");
1127             return AuthorityInfo.NOT_SYNCABLE;
1128         }
1129         if (checkAccountAccess && !canAccessAccount(account, owningPackage, owningUid)) {
1130             Log.w(TAG, "Access to " + logSafe(account) + " denied for package "
1131                     + owningPackage + " in UID " + syncAdapterInfo.uid);
1132             return AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS;
1133         }
1134 
1135         return status;
1136     }
1137 
canAccessAccount(Account account, String packageName, int uid)1138     private boolean canAccessAccount(Account account, String packageName, int uid) {
1139         if (mAccountManager.hasAccountAccess(account, packageName,
1140                 UserHandle.getUserHandleForUid(uid))) {
1141             return true;
1142         }
1143         // We relax the account access rule to also include the system apps as
1144         // they are trusted and we want to minimize the cases where the user
1145         // involvement is required to grant access to the synced account.
1146         try {
1147             mContext.getPackageManager().getApplicationInfoAsUser(packageName,
1148                     PackageManager.MATCH_SYSTEM_ONLY, UserHandle.getUserId(uid));
1149             return true;
1150         } catch (NameNotFoundException e) {
1151             return false;
1152         }
1153     }
1154 
removeSyncsForAuthority(EndPoint info, String why)1155     private void removeSyncsForAuthority(EndPoint info, String why) {
1156         mLogger.log("removeSyncsForAuthority: ", info, why);
1157         verifyJobScheduler();
1158         List<SyncOperation> ops = getAllPendingSyncs();
1159         for (SyncOperation op: ops) {
1160             if (op.target.matchesSpec(info)) {
1161                 mLogger.log("canceling: ", op);
1162                 cancelJob(op, why);
1163             }
1164         }
1165     }
1166 
1167     /**
1168      * Remove a specific periodic sync identified by its target and extras.
1169      */
removePeriodicSync(EndPoint target, Bundle extras, String why)1170     public void removePeriodicSync(EndPoint target, Bundle extras, String why) {
1171         Message m = mSyncHandler.obtainMessage(mSyncHandler.MESSAGE_REMOVE_PERIODIC_SYNC,
1172                 Pair.create(target, why));
1173         m.setData(extras);
1174         m.sendToTarget();
1175     }
1176 
1177     /**
1178      * Add a periodic sync. If a sync with same target and extras exists, its period and
1179      * flexMillis will be updated.
1180      */
updateOrAddPeriodicSync(EndPoint target, long pollFrequency, long flex, Bundle extras)1181     public void updateOrAddPeriodicSync(EndPoint target, long pollFrequency, long flex,
1182             Bundle extras) {
1183         UpdatePeriodicSyncMessagePayload payload = new UpdatePeriodicSyncMessagePayload(target,
1184                 pollFrequency, flex, extras);
1185         mSyncHandler.obtainMessage(SyncHandler.MESSAGE_UPDATE_PERIODIC_SYNC, payload)
1186                 .sendToTarget();
1187     }
1188 
1189     /**
1190      * Get a list of periodic syncs corresponding to the given target.
1191      */
getPeriodicSyncs(EndPoint target)1192     public List<PeriodicSync> getPeriodicSyncs(EndPoint target) {
1193         List<SyncOperation> ops = getAllPendingSyncs();
1194         List<PeriodicSync> periodicSyncs = new ArrayList<PeriodicSync>();
1195 
1196         for (SyncOperation op: ops) {
1197             if (op.isPeriodic && op.target.matchesSpec(target)) {
1198                 periodicSyncs.add(new PeriodicSync(op.target.account, op.target.provider,
1199                         op.getClonedExtras(), op.periodMillis / 1000, op.flexMillis / 1000));
1200             }
1201         }
1202 
1203         return periodicSyncs;
1204     }
1205 
1206     /**
1207      * Schedule sync based on local changes to a provider. We wait for at least LOCAL_SYNC_DELAY
1208      * ms to batch syncs.
1209      */
scheduleLocalSync(Account account, int userId, int reason, String authority, @SyncExemption int syncExemptionFlag, int callingUid, int callingPid, String callingPackage)1210     public void scheduleLocalSync(Account account, int userId, int reason, String authority,
1211             @SyncExemption int syncExemptionFlag,
1212             int callingUid, int callingPid, String callingPackage) {
1213         final Bundle extras = new Bundle();
1214         extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
1215         scheduleSync(account, userId, reason, authority, extras,
1216                 AuthorityInfo.UNDEFINED, LOCAL_SYNC_DELAY, true /* checkIfAccountReady */,
1217                 syncExemptionFlag, callingUid, callingPid, callingPackage);
1218     }
1219 
getSyncAdapterTypes(int callingUid, int userId)1220     public SyncAdapterType[] getSyncAdapterTypes(int callingUid, int userId) {
1221         final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos;
1222         serviceInfos = mSyncAdapters.getAllServices(userId);
1223         final List<SyncAdapterType> types = new ArrayList<>(serviceInfos.size());
1224         for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) {
1225             final String packageName = serviceInfo.type.getPackageName();
1226             if (!TextUtils.isEmpty(packageName) && mPackageManagerInternal.filterAppAccess(
1227                     packageName, callingUid, userId)) {
1228                 continue;
1229             }
1230             types.add(serviceInfo.type);
1231         }
1232         return types.toArray(new SyncAdapterType[] {});
1233     }
1234 
getSyncAdapterPackagesForAuthorityAsUser(String authority, int callingUid, int userId)1235     public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int callingUid,
1236             int userId) {
1237         final String[] syncAdapterPackages = mSyncAdapters.getSyncAdapterPackagesForAuthority(
1238                 authority, userId);
1239         final List<String> filteredResult = new ArrayList<>(syncAdapterPackages.length);
1240         for (String packageName : syncAdapterPackages) {
1241             if (TextUtils.isEmpty(packageName) || mPackageManagerInternal.filterAppAccess(
1242                     packageName, callingUid, userId)) {
1243                 continue;
1244             }
1245             filteredResult.add(packageName);
1246         }
1247         return filteredResult.toArray(new String[] {});
1248     }
1249 
getSyncAdapterPackageAsUser(String accountType, String authority, int callingUid, int userId)1250     public String getSyncAdapterPackageAsUser(String accountType, String authority,
1251             int callingUid, int userId) {
1252         if (accountType == null || authority == null) {
1253             return null;
1254         }
1255         final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
1256                 mSyncAdapters.getServiceInfo(
1257                         SyncAdapterType.newKey(authority, accountType),
1258                         userId);
1259         if (syncAdapterInfo == null) {
1260             return null;
1261         }
1262         final String packageName = syncAdapterInfo.type.getPackageName();
1263         if (TextUtils.isEmpty(packageName) || mPackageManagerInternal.filterAppAccess(
1264                 packageName, callingUid, userId)) {
1265             return null;
1266         }
1267         return packageName;
1268     }
1269 
sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext, SyncResult syncResult)1270     private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext,
1271             SyncResult syncResult) {
1272         if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_SYNC_FINISHED");
1273         Message msg = mSyncHandler.obtainMessage();
1274         msg.what = SyncHandler.MESSAGE_SYNC_FINISHED;
1275         msg.obj = new SyncFinishedOrCancelledMessagePayload(syncContext, syncResult);
1276         mSyncHandler.sendMessage(msg);
1277     }
1278 
sendCancelSyncsMessage(final SyncStorageEngine.EndPoint info, Bundle extras, String why)1279     private void sendCancelSyncsMessage(final SyncStorageEngine.EndPoint info, Bundle extras,
1280             String why) {
1281         if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_CANCEL");
1282 
1283         mLogger.log("sendCancelSyncsMessage() ep=", info, " why=", why);
1284 
1285         Message msg = mSyncHandler.obtainMessage();
1286         msg.what = SyncHandler.MESSAGE_CANCEL;
1287         msg.setData(extras);
1288         msg.obj = info;
1289         mSyncHandler.sendMessage(msg);
1290     }
1291 
1292     /**
1293      * Post a delayed message that will monitor the given sync context by periodically checking how
1294      * much network has been used by the uid.
1295      */
postMonitorSyncProgressMessage(ActiveSyncContext activeSyncContext)1296     private void postMonitorSyncProgressMessage(ActiveSyncContext activeSyncContext) {
1297         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1298             Slog.v(TAG, "posting MESSAGE_SYNC_MONITOR in " +
1299                     (SYNC_MONITOR_WINDOW_LENGTH_MILLIS/1000) + "s");
1300         }
1301 
1302         activeSyncContext.mBytesTransferredAtLastPoll =
1303                 getTotalBytesTransferredByUid(activeSyncContext.mSyncAdapterUid);
1304         activeSyncContext.mLastPolledTimeElapsed = SystemClock.elapsedRealtime();
1305         Message monitorMessage =
1306                 mSyncHandler.obtainMessage(
1307                         SyncHandler.MESSAGE_MONITOR_SYNC,
1308                         activeSyncContext);
1309         mSyncHandler.sendMessageDelayed(monitorMessage, SYNC_MONITOR_WINDOW_LENGTH_MILLIS);
1310     }
1311 
postScheduleSyncMessage(SyncOperation syncOperation, long minDelayMillis)1312     private void postScheduleSyncMessage(SyncOperation syncOperation, long minDelayMillis) {
1313         ScheduleSyncMessagePayload payload =
1314                 new ScheduleSyncMessagePayload(syncOperation, minDelayMillis);
1315         mSyncHandler.obtainMessage(mSyncHandler.MESSAGE_SCHEDULE_SYNC, payload).sendToTarget();
1316     }
1317 
1318     /**
1319      * Monitor sync progress by calculating how many bytes it is managing to send to and fro.
1320      */
getTotalBytesTransferredByUid(int uid)1321     private long getTotalBytesTransferredByUid(int uid) {
1322         return (TrafficStats.getUidRxBytes(uid) + TrafficStats.getUidTxBytes(uid));
1323     }
1324 
1325     /**
1326      * Convenience class for passing parameters for a finished or cancelled sync to the handler
1327      * to be processed.
1328      */
1329     private class SyncFinishedOrCancelledMessagePayload {
1330         public final ActiveSyncContext activeSyncContext;
1331         public final SyncResult syncResult;
1332 
SyncFinishedOrCancelledMessagePayload(ActiveSyncContext syncContext, SyncResult syncResult)1333         SyncFinishedOrCancelledMessagePayload(ActiveSyncContext syncContext,
1334                 SyncResult syncResult) {
1335             this.activeSyncContext = syncContext;
1336             this.syncResult = syncResult;
1337         }
1338     }
1339 
1340     private class UpdatePeriodicSyncMessagePayload {
1341         public final EndPoint target;
1342         public final long pollFrequency;
1343         public final long flex;
1344         public final Bundle extras;
1345 
UpdatePeriodicSyncMessagePayload(EndPoint target, long pollFrequency, long flex, Bundle extras)1346         UpdatePeriodicSyncMessagePayload(EndPoint target, long pollFrequency, long flex,
1347                 Bundle extras) {
1348             this.target = target;
1349             this.pollFrequency = pollFrequency;
1350             this.flex = flex;
1351             this.extras = extras;
1352         }
1353     }
1354 
1355     private static class ScheduleSyncMessagePayload {
1356         final SyncOperation syncOperation;
1357         final long minDelayMillis;
1358 
ScheduleSyncMessagePayload(SyncOperation syncOperation, long minDelayMillis)1359         ScheduleSyncMessagePayload(SyncOperation syncOperation, long minDelayMillis) {
1360             this.syncOperation = syncOperation;
1361             this.minDelayMillis = minDelayMillis;
1362         }
1363     }
1364 
clearBackoffSetting(EndPoint target, String why)1365     private void clearBackoffSetting(EndPoint target, String why) {
1366         Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(target);
1367         if (backoff != null && backoff.first == SyncStorageEngine.NOT_IN_BACKOFF_MODE &&
1368                 backoff.second == SyncStorageEngine.NOT_IN_BACKOFF_MODE) {
1369             return;
1370         }
1371         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1372             Slog.v(TAG, "Clearing backoffs for " + target);
1373         }
1374         mSyncStorageEngine.setBackoff(target,
1375                 SyncStorageEngine.NOT_IN_BACKOFF_MODE,
1376                 SyncStorageEngine.NOT_IN_BACKOFF_MODE);
1377 
1378         rescheduleSyncs(target, why);
1379     }
1380 
increaseBackoffSetting(EndPoint target)1381     private void increaseBackoffSetting(EndPoint target) {
1382         final long now = SystemClock.elapsedRealtime();
1383 
1384         final Pair<Long, Long> previousSettings =
1385                 mSyncStorageEngine.getBackoff(target);
1386         long newDelayInMs = -1;
1387         if (previousSettings != null) {
1388             // Don't increase backoff before current backoff is expired. This will happen for op's
1389             // with ignoreBackoff set.
1390             if (now < previousSettings.first) {
1391                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1392                     Slog.v(TAG, "Still in backoff, do not increase it. "
1393                             + "Remaining: " + ((previousSettings.first - now) / 1000) + " seconds.");
1394                 }
1395                 return;
1396             }
1397             // Subsequent delays are the double of the previous delay.
1398             newDelayInMs =
1399                     (long) (previousSettings.second * mConstants.getRetryTimeIncreaseFactor());
1400         }
1401         if (newDelayInMs <= 0) {
1402             // The initial delay is the jitterized INITIAL_SYNC_RETRY_TIME_IN_MS.
1403             final long initialRetryMs = mConstants.getInitialSyncRetryTimeInSeconds() * 1000;
1404             newDelayInMs = jitterize(initialRetryMs, (long)(initialRetryMs * 1.1));
1405         }
1406 
1407         // Cap the delay.
1408         final long maxSyncRetryTimeInSeconds = mConstants.getMaxSyncRetryTimeInSeconds();
1409 
1410         if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) {
1411             newDelayInMs = maxSyncRetryTimeInSeconds * 1000;
1412         }
1413 
1414         final long backoff = now + newDelayInMs;
1415         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1416             Slog.v(TAG, "Backoff until: " + backoff + ", delayTime: " + newDelayInMs);
1417         }
1418         mSyncStorageEngine.setBackoff(target, backoff, newDelayInMs);
1419         rescheduleSyncs(target, "increaseBackoffSetting");
1420     }
1421 
1422     /**
1423      * Reschedule all scheduled syncs for this EndPoint. The syncs will be scheduled according
1424      * to current backoff and delayUntil values of this EndPoint.
1425      */
rescheduleSyncs(EndPoint target, String why)1426     private void rescheduleSyncs(EndPoint target, String why) {
1427         mLogger.log("rescheduleSyncs() ep=", target, " why=", why);
1428 
1429         List<SyncOperation> ops = getAllPendingSyncs();
1430         int count = 0;
1431         for (SyncOperation op: ops) {
1432             if (!op.isPeriodic && op.target.matchesSpec(target)) {
1433                 count++;
1434                 cancelJob(op, why);
1435                 postScheduleSyncMessage(op, 0 /* min delay */);
1436             }
1437         }
1438         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1439             Slog.v(TAG, "Rescheduled " + count + " syncs for " + target);
1440         }
1441     }
1442 
setDelayUntilTime(EndPoint target, long delayUntilSeconds)1443     private void setDelayUntilTime(EndPoint target, long delayUntilSeconds) {
1444         final long delayUntil = delayUntilSeconds * 1000;
1445         final long absoluteNow = System.currentTimeMillis();
1446         long newDelayUntilTime;
1447         if (delayUntil > absoluteNow) {
1448             newDelayUntilTime = SystemClock.elapsedRealtime() + (delayUntil - absoluteNow);
1449         } else {
1450             newDelayUntilTime = 0;
1451         }
1452         mSyncStorageEngine.setDelayUntilTime(target, newDelayUntilTime);
1453         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1454             Slog.v(TAG, "Delay Until time set to " + newDelayUntilTime + " for " + target);
1455         }
1456         rescheduleSyncs(target, "delayUntil newDelayUntilTime: " + newDelayUntilTime);
1457     }
1458 
isAdapterDelayed(EndPoint target)1459     private boolean isAdapterDelayed(EndPoint target) {
1460         long now = SystemClock.elapsedRealtime();
1461         Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(target);
1462         if (backoff != null && backoff.first != SyncStorageEngine.NOT_IN_BACKOFF_MODE
1463                 && backoff.first > now) {
1464             return true;
1465         }
1466         if (mSyncStorageEngine.getDelayUntilTime(target) > now) {
1467             return true;
1468         }
1469         return false;
1470     }
1471 
1472     /**
1473      * Cancel the active sync if it matches the target.
1474      * @param info object containing info about which syncs to cancel. The target can
1475      * have null account/provider info to specify all accounts/providers.
1476      * @param extras if non-null, specifies the exact sync to remove.
1477      */
cancelActiveSync(SyncStorageEngine.EndPoint info, Bundle extras, String why)1478     public void cancelActiveSync(SyncStorageEngine.EndPoint info, Bundle extras, String why) {
1479         sendCancelSyncsMessage(info, extras, why);
1480     }
1481 
1482     /**
1483      * Schedule a sync operation with JobScheduler.
1484      */
scheduleSyncOperationH(SyncOperation syncOperation)1485     private void scheduleSyncOperationH(SyncOperation syncOperation) {
1486         scheduleSyncOperationH(syncOperation, 0L);
1487     }
1488 
scheduleSyncOperationH(SyncOperation syncOperation, long minDelay)1489     private void scheduleSyncOperationH(SyncOperation syncOperation, long minDelay) {
1490         final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
1491         if (syncOperation == null) {
1492             Slog.e(TAG, "Can't schedule null sync operation.");
1493             return;
1494         }
1495         if (!syncOperation.hasIgnoreBackoff()) {
1496             Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(syncOperation.target);
1497             if (backoff == null) {
1498                 Slog.e(TAG, "Couldn't find backoff values for "
1499                         + logSafe(syncOperation.target));
1500                 backoff = new Pair<Long, Long>(SyncStorageEngine.NOT_IN_BACKOFF_MODE,
1501                         SyncStorageEngine.NOT_IN_BACKOFF_MODE);
1502             } else if (backoff.first != SyncStorageEngine.NOT_IN_BACKOFF_MODE) {
1503                 // if an EJ is being backed-off but doesn't have SYNC_EXTRAS_IGNORE_BACKOFF set,
1504                 // reschedule it as a regular job. Immediately downgrade here in case minDelay is
1505                 // set to 0.
1506                 syncOperation.scheduleEjAsRegularJob = true;
1507             }
1508             long now = SystemClock.elapsedRealtime();
1509             long backoffDelay = backoff.first == SyncStorageEngine.NOT_IN_BACKOFF_MODE ? 0
1510                     : backoff.first - now;
1511             long delayUntil = mSyncStorageEngine.getDelayUntilTime(syncOperation.target);
1512             long delayUntilDelay = delayUntil > now ? delayUntil - now : 0;
1513             if (isLoggable) {
1514                 Slog.v(TAG, "backoff delay:" + backoffDelay
1515                         + " delayUntil delay:" + delayUntilDelay);
1516             }
1517             minDelay = Math.max(minDelay, Math.max(backoffDelay, delayUntilDelay));
1518         }
1519 
1520         if (minDelay < 0) {
1521             minDelay = 0;
1522         } else if (minDelay > 0) {
1523             // We can't apply a delay to an EJ. If we want to delay it, we must demote it to a
1524             // regular job.
1525             syncOperation.scheduleEjAsRegularJob = true;
1526         }
1527 
1528         // Check if duplicate syncs are pending. If found, keep one with least expected run time.
1529 
1530         // If any of the duplicate ones has exemption, then we inherit it.
1531         if (!syncOperation.isPeriodic) {
1532             int inheritedSyncExemptionFlag = ContentResolver.SYNC_EXEMPTION_NONE;
1533 
1534             // Check currently running syncs
1535             for (ActiveSyncContext asc: mActiveSyncContexts) {
1536                 if (asc.mSyncOperation.key.equals(syncOperation.key)) {
1537                     if (isLoggable) {
1538                         Log.v(TAG, "Duplicate sync is already running. Not scheduling "
1539                                 + syncOperation);
1540                     }
1541                     return;
1542                 }
1543             }
1544 
1545             int duplicatesCount = 0;
1546             long now = SystemClock.elapsedRealtime();
1547             syncOperation.expectedRuntime = now + minDelay;
1548             List<SyncOperation> pending = getAllPendingSyncs();
1549             SyncOperation syncToRun = syncOperation;
1550             for (SyncOperation op : pending) {
1551                 if (op.isPeriodic) {
1552                     continue;
1553                 }
1554                 if (op.key.equals(syncOperation.key)) {
1555                     if (syncToRun.expectedRuntime > op.expectedRuntime) {
1556                         syncToRun = op;
1557                     }
1558                     duplicatesCount++;
1559                 }
1560             }
1561             if (duplicatesCount > 1) {
1562                 Slog.wtf(TAG, "duplicates found when scheduling a sync operation: "
1563                         + "owningUid=" + syncOperation.owningUid
1564                         + "; owningPackage=" + syncOperation.owningPackage
1565                         + "; source=" + syncOperation.syncSource
1566                         + "; adapter=" + (syncOperation.target != null
1567                                             ? syncOperation.target.provider
1568                                             : "unknown"));
1569             }
1570 
1571             if (syncOperation != syncToRun) {
1572                 // If there's a duplicate with an earlier run time that's not exempted,
1573                 // and if the current operation is exempted with no minDelay,
1574                 // cancel the duplicate one and keep the current one.
1575                 //
1576                 // This means the duplicate one has a negative expected run time, but it hasn't
1577                 // been executed possibly because of app-standby.
1578 
1579                 if ((minDelay == 0)
1580                         && (syncToRun.syncExemptionFlag < syncOperation.syncExemptionFlag)) {
1581                     syncToRun = syncOperation;
1582                     inheritedSyncExemptionFlag =
1583                             Math.max(inheritedSyncExemptionFlag, syncToRun.syncExemptionFlag);
1584                 }
1585             }
1586 
1587             // Cancel all other duplicate syncs.
1588             for (SyncOperation op : pending) {
1589                 if (op.isPeriodic) {
1590                     continue;
1591                 }
1592                 if (op.key.equals(syncOperation.key)) {
1593                     if (op != syncToRun) {
1594                         if (isLoggable) {
1595                             Slog.v(TAG, "Cancelling duplicate sync " + op);
1596                         }
1597                         inheritedSyncExemptionFlag =
1598                                 Math.max(inheritedSyncExemptionFlag, op.syncExemptionFlag);
1599                         cancelJob(op, "scheduleSyncOperationH-duplicate");
1600                     }
1601                 }
1602             }
1603             if (syncToRun != syncOperation) {
1604                 // Don't schedule because a duplicate sync with earlier expected runtime exists.
1605                 if (isLoggable) {
1606                     Slog.v(TAG, "Not scheduling because a duplicate exists.");
1607                 }
1608 
1609                 // TODO Should we give the winning one SYNC_EXTRAS_APP_STANDBY_EXEMPTED
1610                 // if the current one has it?
1611                 return;
1612             }
1613 
1614             // If any of the duplicates had exemption, we exempt the current one.
1615             //
1616             if (inheritedSyncExemptionFlag > ContentResolver.SYNC_EXEMPTION_NONE) {
1617                 syncOperation.syncExemptionFlag = inheritedSyncExemptionFlag;
1618             }
1619         }
1620 
1621         // Syncs that are re-scheduled shouldn't get a new job id.
1622         if (syncOperation.jobId == SyncOperation.NO_JOB_ID) {
1623             syncOperation.jobId = getUnusedJobIdH();
1624         }
1625 
1626         if (isLoggable) {
1627             Slog.v(TAG, "scheduling sync operation " + syncOperation.toString());
1628         }
1629 
1630         int bias = syncOperation.getJobBias();
1631 
1632         final int networkType = syncOperation.isNotAllowedOnMetered() ?
1633                 JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY;
1634 
1635         // Note this logic means when an exempted sync fails,
1636         // the back-off one will inherit it too, and will be exempted from app-standby.
1637         final int jobFlags = syncOperation.isAppStandbyExempted()
1638                 ? JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY : 0;
1639 
1640         JobInfo.Builder b = new JobInfo.Builder(syncOperation.jobId,
1641                 new ComponentName(mContext, SyncJobService.class))
1642                 .setExtras(syncOperation.toJobInfoExtras())
1643                 .setRequiredNetworkType(networkType)
1644                 .setRequiresStorageNotLow(true)
1645                 .setPersisted(true)
1646                 .setBias(bias)
1647                 .setFlags(jobFlags);
1648 
1649         if (syncOperation.isPeriodic) {
1650             b.setPeriodic(syncOperation.periodMillis, syncOperation.flexMillis);
1651         } else {
1652             if (minDelay > 0) {
1653                 b.setMinimumLatency(minDelay);
1654             }
1655             getSyncStorageEngine().markPending(syncOperation.target, true);
1656         }
1657 
1658         if (syncOperation.hasRequireCharging()) {
1659             b.setRequiresCharging(true);
1660         }
1661 
1662         if (syncOperation.isScheduledAsExpeditedJob() && !syncOperation.scheduleEjAsRegularJob) {
1663             b.setExpedited(true);
1664         }
1665 
1666         if (syncOperation.syncExemptionFlag
1667                 == ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP) {
1668             DeviceIdleInternal dic =
1669                     LocalServices.getService(DeviceIdleInternal.class);
1670             if (dic != null) {
1671                 dic.addPowerSaveTempWhitelistApp(Process.SYSTEM_UID,
1672                         syncOperation.owningPackage,
1673                         mConstants.getKeyExemptionTempWhitelistDurationInSeconds() * 1000,
1674                         TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
1675                         UserHandle.getUserId(syncOperation.owningUid),
1676                         /* sync=*/ false, REASON_SYNC_MANAGER, "sync by top app");
1677             }
1678         }
1679 
1680         final UsageStatsManagerInternal usmi =
1681                 LocalServices.getService(UsageStatsManagerInternal.class);
1682         if (usmi != null) {
1683             // This method name is unfortunate. It elevates apps to a higher bucket, so it ideally
1684             // should be called before we attempt to schedule the job (especially as EJ).
1685             usmi.reportSyncScheduled(syncOperation.owningPackage,
1686                     UserHandle.getUserId(syncOperation.owningUid),
1687                     syncOperation.isAppStandbyExempted());
1688         }
1689 
1690         final JobInfo ji = b.build();
1691         int result = getJobScheduler().scheduleAsPackage(ji, syncOperation.owningPackage,
1692                 syncOperation.target.userId, syncOperation.wakeLockName());
1693         if (result == JobScheduler.RESULT_FAILURE && ji.isExpedited()) {
1694             if (isLoggable) {
1695                 Slog.i(TAG, "Failed to schedule EJ for " + syncOperation.owningPackage
1696                         + ". Downgrading to regular");
1697             }
1698             syncOperation.scheduleEjAsRegularJob = true;
1699             b.setExpedited(false).setExtras(syncOperation.toJobInfoExtras());
1700             result = getJobScheduler().scheduleAsPackage(b.build(), syncOperation.owningPackage,
1701                     syncOperation.target.userId, syncOperation.wakeLockName());
1702         }
1703         if (result == JobScheduler.RESULT_FAILURE) {
1704             Slog.e(TAG, "Failed to schedule job for " + syncOperation.owningPackage);
1705             // TODO: notify AppStandbyController that the sync isn't actually scheduled so the
1706             // bucket doesn't stay elevated
1707         }
1708     }
1709 
1710     /**
1711      * Remove scheduled sync operations.
1712      * @param info limit the removals to operations that match this target. The target can
1713      * have null account/provider info to specify all accounts/providers.
1714      */
clearScheduledSyncOperations(SyncStorageEngine.EndPoint info)1715     public void clearScheduledSyncOperations(SyncStorageEngine.EndPoint info) {
1716         List<SyncOperation> ops = getAllPendingSyncs();
1717         for (SyncOperation op: ops) {
1718             if (!op.isPeriodic && op.target.matchesSpec(info)) {
1719                 cancelJob(op, "clearScheduledSyncOperations");
1720                 getSyncStorageEngine().markPending(op.target, false);
1721             }
1722         }
1723         mSyncStorageEngine.setBackoff(info,
1724                 SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
1725     }
1726 
1727     /**
1728      * Remove a specified sync, if it exists.
1729      * @param info Authority for which the sync is to be removed.
1730      * @param extras extras bundle to uniquely identify sync.
1731      */
cancelScheduledSyncOperation(SyncStorageEngine.EndPoint info, Bundle extras)1732     public void cancelScheduledSyncOperation(SyncStorageEngine.EndPoint info, Bundle extras) {
1733         List<SyncOperation> ops = getAllPendingSyncs();
1734         for (SyncOperation op: ops) {
1735             if (!op.isPeriodic && op.target.matchesSpec(info)
1736                     && op.areExtrasEqual(extras, /*includeSyncSettings=*/ false)) {
1737                 cancelJob(op, "cancelScheduledSyncOperation");
1738             }
1739         }
1740         setAuthorityPendingState(info);
1741         // Reset the back-off if there are no more syncs pending.
1742         if (!mSyncStorageEngine.isSyncPending(info)) {
1743             mSyncStorageEngine.setBackoff(info,
1744                     SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
1745         }
1746     }
1747 
maybeRescheduleSync(SyncResult syncResult, SyncOperation operation)1748     private void maybeRescheduleSync(SyncResult syncResult, SyncOperation operation) {
1749         final boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG);
1750         if (isLoggable) {
1751             Log.d(TAG, "encountered error(s) during the sync: " + syncResult + ", " + operation);
1752         }
1753 
1754         operation.enableBackoff();
1755         // Never run a rescheduled requested-EJ-sync as an EJ.
1756         operation.scheduleEjAsRegularJob = true;
1757 
1758         if (operation.hasDoNotRetry() && !syncResult.syncAlreadyInProgress) {
1759             // syncAlreadyInProgress flag is set by AbstractThreadedSyncAdapter. The sync adapter
1760             // has no way of knowing that a sync error occured. So we DO retry if the error is
1761             // syncAlreadyInProgress.
1762             if (isLoggable) {
1763                 Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified "
1764                         + operation);
1765             }
1766         } else if (operation.isUpload() && !syncResult.syncAlreadyInProgress) {
1767             // If this was an upward sync then schedule a two-way sync immediately.
1768             operation.enableTwoWaySync();
1769             if (isLoggable) {
1770                 Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync "
1771                         + "encountered an error: " + operation);
1772             }
1773             scheduleSyncOperationH(operation);
1774         } else if (syncResult.tooManyRetries) {
1775             // If this sync aborted because the internal sync loop retried too many times then
1776             //   don't reschedule. Otherwise we risk getting into a retry loop.
1777             if (isLoggable) {
1778                 Log.d(TAG, "not retrying sync operation because it retried too many times: "
1779                         + operation);
1780             }
1781         } else if (syncResult.madeSomeProgress()) {
1782             // If the operation succeeded to some extent then retry immediately.
1783             if (isLoggable) {
1784                 Log.d(TAG, "retrying sync operation because even though it had an error "
1785                         + "it achieved some success");
1786             }
1787             scheduleSyncOperationH(operation);
1788         } else if (syncResult.syncAlreadyInProgress) {
1789             if (isLoggable) {
1790                 Log.d(TAG, "retrying sync operation that failed because there was already a "
1791                         + "sync in progress: " + operation);
1792             }
1793             scheduleSyncOperationH(operation, DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000);
1794         } else if (syncResult.hasSoftError()) {
1795             // If this was a two-way sync then retry soft errors with an exponential backoff.
1796             if (isLoggable) {
1797                 Log.d(TAG, "retrying sync operation because it encountered a soft error: "
1798                         + operation);
1799             }
1800             scheduleSyncOperationH(operation);
1801         } else {
1802             // Otherwise do not reschedule.
1803             Log.e(TAG, "not retrying sync operation because the error is a hard error: "
1804                     + logSafe(operation));
1805         }
1806     }
1807 
onUserUnlocked(int userId)1808     private void onUserUnlocked(int userId) {
1809         // Make sure that accounts we're about to use are valid.
1810         AccountManagerService.getSingleton().validateAccounts(userId);
1811 
1812         mSyncAdapters.invalidateCache(userId);
1813 
1814         EndPoint target = new EndPoint(null, null, userId);
1815         updateRunningAccounts(target);
1816 
1817         // Schedule sync for any accounts under started user, but only the NOT_INITIALIZED adapters.
1818         final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId,
1819                 mContext.getOpPackageName());
1820         for (Account account : accounts) {
1821             scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null,
1822                     AuthorityInfo.NOT_INITIALIZED, ContentResolver.SYNC_EXEMPTION_NONE,
1823                     Process.myUid(), -3, null);
1824         }
1825     }
1826 
onUserStopped(int userId)1827     private void onUserStopped(int userId) {
1828         updateRunningAccounts(null /* Don't sync any target */);
1829 
1830         cancelActiveSync(
1831                 new SyncStorageEngine.EndPoint(
1832                         null /* any account */,
1833                         null /* any authority */,
1834                         userId),
1835                 null /* any sync. */,
1836                 "onUserStopped"
1837         );
1838     }
1839 
onUserRemoved(int userId)1840     private void onUserRemoved(int userId) {
1841         mLogger.log("onUserRemoved: u", userId);
1842         updateRunningAccounts(null /* Don't sync any target */);
1843 
1844         // Clean up the storage engine database
1845         mSyncStorageEngine.removeStaleAccounts(null, userId);
1846         List<SyncOperation> ops = getAllPendingSyncs();
1847         for (SyncOperation op: ops) {
1848             if (op.target.userId == userId) {
1849                 cancelJob(op, "user removed u" + userId);
1850             }
1851         }
1852     }
1853 
1854     /**
1855      * Construct intent used to bind to an adapter.
1856      *
1857      * @param context Context to create intent for
1858      * @param syncAdapterComponent The adapter description
1859      * @param userId The user the adapter belongs to
1860      *
1861      * @return The intent required to bind to the adapter
1862      */
getAdapterBindIntent(@onNull Context context, @NonNull ComponentName syncAdapterComponent, @UserIdInt int userId)1863     static @NonNull Intent getAdapterBindIntent(@NonNull Context context,
1864             @NonNull ComponentName syncAdapterComponent, @UserIdInt int userId) {
1865         final Intent intent = new Intent();
1866         intent.setAction("android.content.SyncAdapter");
1867         intent.setComponent(syncAdapterComponent);
1868         intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
1869                 com.android.internal.R.string.sync_binding_label);
1870         intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(context, 0,
1871                 new Intent(Settings.ACTION_SYNC_SETTINGS), PendingIntent.FLAG_IMMUTABLE, null,
1872                 UserHandle.of(userId)));
1873 
1874         return intent;
1875     }
1876 
1877     /**
1878      * @hide
1879      */
1880     class ActiveSyncContext extends ISyncContext.Stub
1881             implements ServiceConnection, IBinder.DeathRecipient {
1882         final SyncOperation mSyncOperation;
1883         final long mHistoryRowId;
1884         ISyncAdapter mSyncAdapter;
1885         final long mStartTime;
1886         long mTimeoutStartTime;
1887         boolean mBound;
1888         final PowerManager.WakeLock mSyncWakeLock;
1889         final int mSyncAdapterUid;
1890         SyncInfo mSyncInfo;
1891         boolean mIsLinkedToDeath = false;
1892         String mEventName;
1893 
1894         /** Total bytes transferred, counted at {@link #mLastPolledTimeElapsed} */
1895         long mBytesTransferredAtLastPoll;
1896         /**
1897          * Last point in {@link SystemClock#elapsedRealtime()} at which we checked the # of bytes
1898          * transferred to/fro by this adapter.
1899          */
1900         long mLastPolledTimeElapsed;
1901 
1902         /**
1903          * Create an ActiveSyncContext for an impending sync and grab the wakelock for that
1904          * sync adapter. Since this grabs the wakelock you need to be sure to call
1905          * close() when you are done with this ActiveSyncContext, whether the sync succeeded
1906          * or not.
1907          * @param syncOperation the SyncOperation we are about to sync
1908          * @param historyRowId the row in which to record the history info for this sync
1909          * @param syncAdapterUid the UID of the application that contains the sync adapter
1910          * for this sync. This is used to attribute the wakelock hold to that application.
1911          */
ActiveSyncContext(SyncOperation syncOperation, long historyRowId, int syncAdapterUid)1912         public ActiveSyncContext(SyncOperation syncOperation, long historyRowId,
1913                 int syncAdapterUid) {
1914             super();
1915             mSyncAdapterUid = syncAdapterUid;
1916             mSyncOperation = syncOperation;
1917             mHistoryRowId = historyRowId;
1918             mSyncAdapter = null;
1919             mStartTime = SystemClock.elapsedRealtime();
1920             mTimeoutStartTime = mStartTime;
1921             mSyncWakeLock = mSyncHandler.getSyncWakeLock(mSyncOperation);
1922             mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterUid));
1923             mSyncWakeLock.acquire();
1924         }
1925 
sendHeartbeat()1926         public void sendHeartbeat() {
1927             // Heartbeats are no longer used.
1928         }
1929 
onFinished(SyncResult result)1930         public void onFinished(SyncResult result) {
1931             if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "onFinished: " + this);
1932             // Include "this" in the message so that the handler can ignore it if this
1933             // ActiveSyncContext is no longer the mActiveSyncContext at message handling
1934             // time.
1935             mLogger.log("onFinished result=", result, " endpoint=",
1936                     (mSyncOperation == null ? "null" : mSyncOperation.target));
1937             sendSyncFinishedOrCanceledMessage(this, result);
1938         }
1939 
toString(StringBuilder sb, boolean logSafe)1940         public void toString(StringBuilder sb, boolean logSafe) {
1941             sb.append("startTime ").append(mStartTime)
1942                     .append(", mTimeoutStartTime ").append(mTimeoutStartTime)
1943                     .append(", mHistoryRowId ").append(mHistoryRowId)
1944                     .append(", syncOperation ").append(
1945                         logSafe ? logSafe(mSyncOperation) : mSyncOperation);
1946         }
1947 
onServiceConnected(ComponentName name, IBinder service)1948         public void onServiceConnected(ComponentName name, IBinder service) {
1949             Message msg = mSyncHandler.obtainMessage();
1950             msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED;
1951             msg.obj = new ServiceConnectionData(this, service);
1952             mSyncHandler.sendMessage(msg);
1953         }
1954 
onServiceDisconnected(ComponentName name)1955         public void onServiceDisconnected(ComponentName name) {
1956             Message msg = mSyncHandler.obtainMessage();
1957             msg.what = SyncHandler.MESSAGE_SERVICE_DISCONNECTED;
1958             msg.obj = new ServiceConnectionData(this, null);
1959             mSyncHandler.sendMessage(msg);
1960         }
1961 
bindToSyncAdapter(ComponentName serviceComponent, int userId)1962         boolean bindToSyncAdapter(ComponentName serviceComponent, int userId) {
1963             if (Log.isLoggable(TAG, Log.VERBOSE)) {
1964                 Log.d(TAG, "bindToSyncAdapter: " + serviceComponent + ", connection " + this);
1965             }
1966             Intent intent = getAdapterBindIntent(mContext, serviceComponent, userId);
1967 
1968             mBound = true;
1969             final boolean bindResult = mContext.bindServiceAsUser(intent, this,
1970                     SYNC_ADAPTER_CONNECTION_FLAGS, new UserHandle(mSyncOperation.target.userId));
1971             mLogger.log("bindService() returned=", mBound, " for ", this);
1972             if (!bindResult) {
1973                 mBound = false;
1974             } else {
1975                 try {
1976                     mEventName = mSyncOperation.wakeLockName();
1977                     mBatteryStats.noteSyncStart(mEventName, mSyncAdapterUid);
1978                 } catch (RemoteException e) {
1979                 }
1980             }
1981             return bindResult;
1982         }
1983 
1984         /**
1985          * Performs the required cleanup, which is the releasing of the wakelock and
1986          * unbinding from the sync adapter (if actually bound).
1987          */
close()1988         protected void close() {
1989             if (Log.isLoggable(TAG, Log.VERBOSE)) {
1990                 Log.d(TAG, "unBindFromSyncAdapter: connection " + this);
1991             }
1992             if (mBound) {
1993                 mBound = false;
1994                 mLogger.log("unbindService for ", this);
1995                 mContext.unbindService(this);
1996                 try {
1997                     mBatteryStats.noteSyncFinish(mEventName, mSyncAdapterUid);
1998                 } catch (RemoteException e) {
1999                 }
2000             }
2001             mSyncWakeLock.release();
2002             mSyncWakeLock.setWorkSource(null);
2003         }
2004 
toString()2005         public String toString() {
2006             StringBuilder sb = new StringBuilder();
2007             toString(sb, false);
2008             return sb.toString();
2009         }
2010 
toSafeString()2011         public String toSafeString() {
2012             StringBuilder sb = new StringBuilder();
2013             toString(sb, true);
2014             return sb.toString();
2015         }
2016 
2017         @Override
binderDied()2018         public void binderDied() {
2019             sendSyncFinishedOrCanceledMessage(this, null);
2020         }
2021     }
2022 
dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll)2023     protected void dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll) {
2024         final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
2025 
2026         final SyncAdapterStateFetcher buckets = new SyncAdapterStateFetcher();
2027 
2028         dumpSyncState(ipw, buckets);
2029         mConstants.dump(pw, "");
2030         dumpSyncAdapters(ipw);
2031 
2032         if (dumpAll) {
2033             ipw.println("Detailed Sync History");
2034             mLogger.dumpAll(pw);
2035         }
2036     }
2037 
formatTime(long time)2038     static String formatTime(long time) {
2039         if (time == 0) {
2040             return "N/A";
2041         }
2042         return TimeMigrationUtils.formatMillisWithFixedFormat(time);
2043     }
2044 
2045     private final static Comparator<SyncOperation> sOpDumpComparator = (op1, op2) -> {
2046         int res = Integer.compare(op1.target.userId, op2.target.userId);
2047         if (res != 0) return res;
2048 
2049         final Comparator<String> stringComparator = String.CASE_INSENSITIVE_ORDER;
2050 
2051         res = stringComparator.compare(op1.target.account.type, op2.target.account.type);
2052         if (res != 0) return res;
2053 
2054         res = stringComparator.compare(op1.target.account.name, op2.target.account.name);
2055         if (res != 0) return res;
2056 
2057         res = stringComparator.compare(op1.target.provider, op2.target.provider);
2058         if (res != 0) return res;
2059 
2060         res = Integer.compare(op1.reason, op2.reason);
2061         if (res != 0) return res;
2062 
2063         res = Long.compare(op1.periodMillis, op2.periodMillis);
2064         if (res != 0) return res;
2065 
2066         res = Long.compare(op1.expectedRuntime, op2.expectedRuntime);
2067         if (res != 0) return res;
2068 
2069         res = Long.compare(op1.jobId, op2.jobId);
2070         if (res != 0) return res;
2071 
2072         return 0;
2073     };
2074 
2075     private final static Comparator<SyncOperation> sOpRuntimeComparator = (op1, op2) -> {
2076         int res = Long.compare(op1.expectedRuntime, op2.expectedRuntime);
2077         if (res != 0) return res;
2078 
2079         return sOpDumpComparator.compare(op1, op2);
2080     };
2081 
countIf(Collection<T> col, Predicate<T> p)2082     private static <T> int countIf(Collection<T> col, Predicate<T> p) {
2083         int ret = 0;
2084         for (T item : col) {
2085             if (p.test(item)) ret++;
2086         }
2087         return ret;
2088     }
2089 
dumpPendingSyncs(PrintWriter pw, SyncAdapterStateFetcher buckets)2090     protected void dumpPendingSyncs(PrintWriter pw, SyncAdapterStateFetcher buckets) {
2091         List<SyncOperation> pendingSyncs = getAllPendingSyncs();
2092 
2093         pw.print("Pending Syncs: ");
2094         pw.println(countIf(pendingSyncs, op -> !op.isPeriodic));
2095 
2096         Collections.sort(pendingSyncs, sOpRuntimeComparator);
2097         int count = 0;
2098         for (SyncOperation op: pendingSyncs) {
2099             if (!op.isPeriodic) {
2100                 pw.println(op.dump(null, false, buckets, /*logSafe=*/ false));
2101                 count++;
2102             }
2103         }
2104         pw.println();
2105     }
2106 
dumpPeriodicSyncs(PrintWriter pw, SyncAdapterStateFetcher buckets)2107     protected void dumpPeriodicSyncs(PrintWriter pw, SyncAdapterStateFetcher buckets) {
2108         List<SyncOperation> pendingSyncs = getAllPendingSyncs();
2109 
2110         pw.print("Periodic Syncs: ");
2111         pw.println(countIf(pendingSyncs, op -> op.isPeriodic));
2112 
2113         Collections.sort(pendingSyncs, sOpDumpComparator);
2114         int count = 0;
2115         for (SyncOperation op: pendingSyncs) {
2116             if (op.isPeriodic) {
2117                 pw.println(op.dump(null, false, buckets, /*logSafe=*/ false));
2118                 count++;
2119             }
2120         }
2121         pw.println();
2122     }
2123 
2124     /**
2125      * Similar to {@link android.util.TimeUtils#formatDuration}, but it's more suitable and concise
2126      * for the sync manager dumpsys.  (Don't add the leading + sign, don't show milliseconds.)
2127      */
formatDurationHMS(StringBuilder sb, long duration)2128     public static StringBuilder formatDurationHMS(StringBuilder sb, long duration) {
2129         duration /= 1000;
2130         if (duration < 0) {
2131             sb.append('-');
2132             duration = -duration;
2133         }
2134         final long seconds = duration % 60;
2135         duration /= 60;
2136 
2137         final long minutes = duration % 60;
2138         duration /= 60;
2139 
2140         final long hours = duration % 24;
2141         duration /= 24;
2142 
2143         final long days = duration;
2144 
2145         boolean print = false;
2146         if (days > 0) {
2147             sb.append(days);
2148             sb.append('d');
2149             print = true;
2150         }
2151         print = printTwoDigitNumber(sb, hours, 'h', print);
2152         print = printTwoDigitNumber(sb, minutes, 'm', print);
2153         print = printTwoDigitNumber(sb, seconds, 's', print);
2154         if (!print) {
2155             sb.append("0s");
2156         }
2157 
2158         return sb;
2159     }
2160 
printTwoDigitNumber(StringBuilder sb, long value, char unit, boolean always)2161     private static boolean printTwoDigitNumber(StringBuilder sb, long value, char unit,
2162             boolean always) {
2163         if (!always && (value == 0)) {
2164             return false;
2165         }
2166         if (always && (value < 10)) {
2167             sb.append('0');
2168         }
2169         sb.append(value);
2170         sb.append(unit);
2171         return true;
2172     }
2173 
2174     @NeverCompile // Avoid size overhead of debugging code.
dumpSyncState(PrintWriter pw, SyncAdapterStateFetcher buckets)2175     protected void dumpSyncState(PrintWriter pw, SyncAdapterStateFetcher buckets) {
2176         final StringBuilder sb = new StringBuilder();
2177 
2178         pw.print("Data connected: "); pw.println(mDataConnectionIsConnected);
2179         pw.print("Battery saver: ");
2180         pw.println((mPowerManager != null) && mPowerManager.isPowerSaveMode());
2181 
2182         pw.print("Background network restriction: ");
2183         {
2184             final ConnectivityManager cm = getConnectivityManager();
2185             final int status = (cm == null) ? -1 : cm.getRestrictBackgroundStatus();
2186             switch (status) {
2187                 case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED:
2188                     pw.println(" disabled");
2189                     break;
2190                 case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED:
2191                     pw.println(" whitelisted");
2192                     break;
2193                 case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED:
2194                     pw.println(" enabled");
2195                     break;
2196                 default:
2197                     pw.print("Unknown(");
2198                     pw.print(status);
2199                     pw.println(")");
2200                     break;
2201             }
2202         }
2203 
2204         pw.print("Auto sync: ");
2205         List<UserInfo> users = getAllUsers();
2206         if (users != null) {
2207             for (UserInfo user : users) {
2208                 pw.print("u" + user.id + "="
2209                         + mSyncStorageEngine.getMasterSyncAutomatically(user.id) + " ");
2210             }
2211             pw.println();
2212         }
2213         Intent storageLowIntent =
2214                 mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW));
2215         pw.print("Storage low: "); pw.println(storageLowIntent != null);
2216         pw.print("Clock valid: "); pw.println(mSyncStorageEngine.isClockValid());
2217 
2218         final AccountAndUser[] accounts =
2219                 AccountManagerService.getSingleton().getAllAccountsForSystemProcess();
2220 
2221         pw.print("Accounts: ");
2222         if (accounts != INITIAL_ACCOUNTS_ARRAY) {
2223             pw.println(accounts.length);
2224         } else {
2225             pw.println("not known yet");
2226         }
2227         final long now = SystemClock.elapsedRealtime();
2228         pw.print("Now: "); pw.print(now);
2229         pw.println(" (" + formatTime(System.currentTimeMillis()) + ")");
2230 
2231         sb.setLength(0);
2232         pw.print("Uptime: "); pw.print(formatDurationHMS(sb, now));
2233         pw.println();
2234         pw.print("Time spent syncing: ");
2235 
2236         sb.setLength(0);
2237         pw.print(formatDurationHMS(sb,
2238                 mSyncHandler.mSyncTimeTracker.timeSpentSyncing()));
2239         pw.print(", sync ");
2240         pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not ");
2241         pw.println("in progress");
2242 
2243         pw.println();
2244         pw.println("Active Syncs: " + mActiveSyncContexts.size());
2245         final PackageManager pm = mContext.getPackageManager();
2246         for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
2247             final long durationInSeconds = (now - activeSyncContext.mStartTime);
2248             pw.print("  ");
2249             sb.setLength(0);
2250             pw.print(formatDurationHMS(sb, durationInSeconds));
2251             pw.print(" - ");
2252             pw.print(activeSyncContext.mSyncOperation.dump(pm, false, buckets, /*logSafe=*/ false));
2253             pw.println();
2254         }
2255         pw.println();
2256 
2257         dumpPendingSyncs(pw, buckets);
2258         dumpPeriodicSyncs(pw, buckets);
2259 
2260         // Join the installed sync adapter with the accounts list and emit for everything.
2261         pw.println("Sync Status");
2262 
2263         final ArrayList<Pair<EndPoint, SyncStatusInfo>> statuses = new ArrayList<>();
2264 
2265         mSyncStorageEngine.resetTodayStats(/* force=*/ false);
2266 
2267         for (AccountAndUser account : accounts) {
2268             final boolean unlocked;
2269             synchronized (mUnlockedUsers) {
2270                 unlocked = mUnlockedUsers.get(account.userId);
2271             }
2272             pw.printf("Account %s u%d %s%s\n",
2273                     account.account.name, account.userId, account.account.type,
2274                     (unlocked ? "" : " (locked)"));
2275 
2276             pw.println("=======================================================================");
2277             final PrintTable table = new PrintTable(16);
2278             table.set(0, 0,
2279                     "Authority", // 0
2280                     "Syncable",  // 1
2281                     "Enabled",   // 2
2282 
2283                     "Stats",     // 3 "Total", "Today" or "Yesterday".
2284 
2285                     "Loc",       // 4 # of syncs with local sources. (including failures/cancels. )
2286                     "Poll",      // 5 "poll" syncs.
2287                     "Per",       // 6 Periodic syncs.
2288                     "Feed",      // 7 Syncs with a "feed" extra. (subscribedfeeds?)
2289                     "User",      // 8 User-initiated
2290                     "Othr",      // 9 Other sources.
2291 
2292                     "Tot",       // 10 Total syncs (including failures / cancels)
2293                     "Fail",      // 11 (Failure)
2294                     "Can",       // 12 (Cancel)
2295 
2296                     "Time",      // 13 Total time
2297                     "Last Sync", // 14
2298                     "Backoff"    // 15
2299             );
2300 
2301             final List<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> sorted =
2302                     Lists.newArrayList();
2303             sorted.addAll(mSyncAdapters.getAllServices(account.userId));
2304             Collections.sort(sorted,
2305                     new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() {
2306                         @Override
2307                         public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs,
2308                                 RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
2309                             return lhs.type.authority.compareTo(rhs.type.authority);
2310                         }
2311                     });
2312             for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : sorted) {
2313                 if (!syncAdapterType.type.accountType.equals(account.account.type)) {
2314                     continue;
2315                 }
2316                 int row = table.getNumRows();
2317                 Pair<AuthorityInfo, SyncStatusInfo> syncAuthoritySyncStatus =
2318                         mSyncStorageEngine.getCopyOfAuthorityWithSyncStatus(
2319                                 new SyncStorageEngine.EndPoint(
2320                                         account.account,
2321                                         syncAdapterType.type.authority,
2322                                         account.userId));
2323                 SyncStorageEngine.AuthorityInfo settings = syncAuthoritySyncStatus.first;
2324                 SyncStatusInfo status = syncAuthoritySyncStatus.second;
2325                 statuses.add(Pair.create(settings.target, status));
2326                 String authority = settings.target.provider;
2327                 if (authority.length() > 50) {
2328                     authority = authority.substring(authority.length() - 50);
2329                 }
2330                 table.set(row, 0, authority, settings.syncable, settings.enabled);
2331 
2332                 QuadConsumer<String, Stats, Function<Integer, String>, Integer> c =
2333                         (label, stats, filter, r) -> {
2334                     sb.setLength(0);
2335                     table.set(r, 3,
2336                             label,
2337                             filter.apply(stats.numSourceLocal),
2338                             filter.apply(stats.numSourcePoll),
2339                             filter.apply(stats.numSourcePeriodic),
2340                             filter.apply(stats.numSourceFeed),
2341                             filter.apply(stats.numSourceUser),
2342                             filter.apply(stats.numSourceOther),
2343                             filter.apply(stats.numSyncs),
2344                             filter.apply(stats.numFailures),
2345                             filter.apply(stats.numCancels),
2346                             formatDurationHMS(sb, stats.totalElapsedTime));
2347                 };
2348                 c.accept("Total", status.totalStats, (i) -> Integer.toString(i), row);
2349                 c.accept("Today", status.todayStats, this::zeroToEmpty, row + 1);
2350                 c.accept("Yestr", status.yesterdayStats, this::zeroToEmpty, row + 2);
2351 
2352                 final int LAST_SYNC = 14;
2353                 final int BACKOFF = LAST_SYNC + 1;
2354 
2355                 int row1 = row;
2356                 if (settings.delayUntil > now) {
2357                     table.set(row1++, BACKOFF, "D: " + (settings.delayUntil - now) / 1000);
2358                     if (settings.backoffTime > now) {
2359                         table.set(row1++, BACKOFF, "B: " + (settings.backoffTime - now) / 1000);
2360                         table.set(row1++, BACKOFF, settings.backoffDelay / 1000);
2361                     }
2362                 }
2363 
2364                 row1 = row;
2365                 if (status.lastSuccessTime != 0) {
2366                     table.set(row1++, LAST_SYNC, SyncStorageEngine.SOURCES[status.lastSuccessSource]
2367                             + " " + "SUCCESS");
2368                     table.set(row1++, LAST_SYNC, formatTime(status.lastSuccessTime));
2369                 }
2370                 if (status.lastFailureTime != 0) {
2371                     table.set(row1++, LAST_SYNC, SyncStorageEngine.SOURCES[status.lastFailureSource]
2372                             + " " + "FAILURE");
2373                     table.set(row1++, LAST_SYNC, formatTime(status.lastFailureTime));
2374                     //noinspection UnusedAssignment
2375                     table.set(row1++, LAST_SYNC, status.lastFailureMesg);
2376                 }
2377             }
2378             table.writeTo(pw);
2379         }
2380 
2381         dumpSyncHistory(pw);
2382 
2383         pw.println();
2384         pw.println("Per Adapter History");
2385         pw.println("(SERVER is now split up to FEED and OTHER)");
2386 
2387         for (int i = 0; i < statuses.size(); i++) {
2388             final Pair<EndPoint, SyncStatusInfo> event = statuses.get(i);
2389 
2390             pw.print("  ");
2391             pw.print(event.first.account.name);
2392             pw.print('/');
2393             pw.print(event.first.account.type);
2394             pw.print(" u");
2395             pw.print(event.first.userId);
2396             pw.print(" [");
2397             pw.print(event.first.provider);
2398             pw.print("]");
2399             pw.println();
2400 
2401             pw.println("    Per source last syncs:");
2402             for (int j = 0; j < SyncStorageEngine.SOURCES.length; j++) {
2403                 pw.print("      ");
2404                 pw.print(String.format("%8s", SyncStorageEngine.SOURCES[j]));
2405                 pw.print("  Success: ");
2406                 pw.print(formatTime(event.second.perSourceLastSuccessTimes[j]));
2407 
2408                 pw.print("  Failure: ");
2409                 pw.println(formatTime(event.second.perSourceLastFailureTimes[j]));
2410             }
2411 
2412             pw.println("    Last syncs:");
2413             for (int j = 0; j < event.second.getEventCount(); j++) {
2414                 pw.print("      ");
2415                 pw.print(formatTime(event.second.getEventTime(j)));
2416                 pw.print(' ');
2417                 pw.print(event.second.getEvent(j));
2418                 pw.println();
2419             }
2420             if (event.second.getEventCount() == 0) {
2421                 pw.println("      N/A");
2422             }
2423         }
2424     }
2425 
zeroToEmpty(int value)2426     private String zeroToEmpty(int value) {
2427         return (value != 0) ? Integer.toString(value) : "";
2428     }
2429 
dumpTimeSec(PrintWriter pw, long time)2430     private void dumpTimeSec(PrintWriter pw, long time) {
2431         pw.print(time/1000); pw.print('.'); pw.print((time/100)%10);
2432         pw.print('s');
2433     }
2434 
dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds)2435     private void dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds) {
2436         pw.print("Success ("); pw.print(ds.successCount);
2437         if (ds.successCount > 0) {
2438             pw.print(" for "); dumpTimeSec(pw, ds.successTime);
2439             pw.print(" avg="); dumpTimeSec(pw, ds.successTime/ds.successCount);
2440         }
2441         pw.print(") Failure ("); pw.print(ds.failureCount);
2442         if (ds.failureCount > 0) {
2443             pw.print(" for "); dumpTimeSec(pw, ds.failureTime);
2444             pw.print(" avg="); dumpTimeSec(pw, ds.failureTime/ds.failureCount);
2445         }
2446         pw.println(")");
2447     }
2448 
dumpSyncHistory(PrintWriter pw)2449     protected void dumpSyncHistory(PrintWriter pw) {
2450         dumpRecentHistory(pw);
2451         dumpDayStatistics(pw);
2452     }
2453 
dumpRecentHistory(PrintWriter pw)2454     private void dumpRecentHistory(PrintWriter pw) {
2455         final ArrayList<SyncStorageEngine.SyncHistoryItem> items
2456                 = mSyncStorageEngine.getSyncHistory();
2457         if (items != null && items.size() > 0) {
2458             final Map<String, AuthoritySyncStats> authorityMap = Maps.newHashMap();
2459             long totalElapsedTime = 0;
2460             long totalTimes = 0;
2461             final int N = items.size();
2462 
2463             int maxAuthority = 0;
2464             int maxAccount = 0;
2465             for (SyncStorageEngine.SyncHistoryItem item : items) {
2466                 SyncStorageEngine.AuthorityInfo authorityInfo
2467                         = mSyncStorageEngine.getAuthority(item.authorityId);
2468                 final String authorityName;
2469                 final String accountKey;
2470                 if (authorityInfo != null) {
2471                     authorityName = authorityInfo.target.provider;
2472                     accountKey = authorityInfo.target.account.name + "/"
2473                             + authorityInfo.target.account.type
2474                             + " u" + authorityInfo.target.userId;
2475                 } else {
2476                     authorityName = "Unknown";
2477                     accountKey = "Unknown";
2478                 }
2479 
2480                 int length = authorityName.length();
2481                 if (length > maxAuthority) {
2482                     maxAuthority = length;
2483                 }
2484                 length = accountKey.length();
2485                 if (length > maxAccount) {
2486                     maxAccount = length;
2487                 }
2488 
2489                 final long elapsedTime = item.elapsedTime;
2490                 totalElapsedTime += elapsedTime;
2491                 totalTimes++;
2492                 AuthoritySyncStats authoritySyncStats = authorityMap.get(authorityName);
2493                 if (authoritySyncStats == null) {
2494                     authoritySyncStats = new AuthoritySyncStats(authorityName);
2495                     authorityMap.put(authorityName, authoritySyncStats);
2496                 }
2497                 authoritySyncStats.elapsedTime += elapsedTime;
2498                 authoritySyncStats.times++;
2499                 final Map<String, AccountSyncStats> accountMap = authoritySyncStats.accountMap;
2500                 AccountSyncStats accountSyncStats = accountMap.get(accountKey);
2501                 if (accountSyncStats == null) {
2502                     accountSyncStats = new AccountSyncStats(accountKey);
2503                     accountMap.put(accountKey, accountSyncStats);
2504                 }
2505                 accountSyncStats.elapsedTime += elapsedTime;
2506                 accountSyncStats.times++;
2507 
2508             }
2509 
2510             if (totalElapsedTime > 0) {
2511                 pw.println();
2512                 pw.printf("Detailed Statistics (Recent history):  "
2513                                 + "%d (# of times) %ds (sync time)\n",
2514                         totalTimes, totalElapsedTime / 1000);
2515 
2516                 final List<AuthoritySyncStats> sortedAuthorities =
2517                         new ArrayList<AuthoritySyncStats>(authorityMap.values());
2518                 Collections.sort(sortedAuthorities, new Comparator<AuthoritySyncStats>() {
2519                     @Override
2520                     public int compare(AuthoritySyncStats lhs, AuthoritySyncStats rhs) {
2521                         // reverse order
2522                         int compare = Integer.compare(rhs.times, lhs.times);
2523                         if (compare == 0) {
2524                             compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime);
2525                         }
2526                         return compare;
2527                     }
2528                 });
2529 
2530                 final int maxLength = Math.max(maxAuthority, maxAccount + 3);
2531                 final int padLength = 2 + 2 + maxLength + 2 + 10 + 11;
2532                 final char chars[] = new char[padLength];
2533                 Arrays.fill(chars, '-');
2534                 final String separator = new String(chars);
2535 
2536                 final String authorityFormat =
2537                         String.format("  %%-%ds: %%-9s  %%-11s\n", maxLength + 2);
2538                 final String accountFormat =
2539                         String.format("    %%-%ds:   %%-9s  %%-11s\n", maxLength);
2540 
2541                 pw.println(separator);
2542                 for (AuthoritySyncStats authoritySyncStats : sortedAuthorities) {
2543                     String name = authoritySyncStats.name;
2544                     long elapsedTime;
2545                     int times;
2546                     String timeStr;
2547                     String timesStr;
2548 
2549                     elapsedTime = authoritySyncStats.elapsedTime;
2550                     times = authoritySyncStats.times;
2551                     timeStr = String.format("%ds/%d%%",
2552                             elapsedTime / 1000,
2553                             elapsedTime * 100 / totalElapsedTime);
2554                     timesStr = String.format("%d/%d%%",
2555                             times,
2556                             times * 100 / totalTimes);
2557                     pw.printf(authorityFormat, name, timesStr, timeStr);
2558 
2559                     final List<AccountSyncStats> sortedAccounts =
2560                             new ArrayList<AccountSyncStats>(
2561                                     authoritySyncStats.accountMap.values());
2562                     Collections.sort(sortedAccounts, new Comparator<AccountSyncStats>() {
2563                         @Override
2564                         public int compare(AccountSyncStats lhs, AccountSyncStats rhs) {
2565                             // reverse order
2566                             int compare = Integer.compare(rhs.times, lhs.times);
2567                             if (compare == 0) {
2568                                 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime);
2569                             }
2570                             return compare;
2571                         }
2572                     });
2573                     for (AccountSyncStats stats: sortedAccounts) {
2574                         elapsedTime = stats.elapsedTime;
2575                         times = stats.times;
2576                         timeStr = String.format("%ds/%d%%",
2577                                 elapsedTime / 1000,
2578                                 elapsedTime * 100 / totalElapsedTime);
2579                         timesStr = String.format("%d/%d%%",
2580                                 times,
2581                                 times * 100 / totalTimes);
2582                         pw.printf(accountFormat, stats.name, timesStr, timeStr);
2583                     }
2584                     pw.println(separator);
2585                 }
2586             }
2587 
2588             pw.println();
2589             pw.println("Recent Sync History");
2590             pw.println("(SERVER is now split up to FEED and OTHER)");
2591             final String format = "  %-" + maxAccount + "s  %-" + maxAuthority + "s %s\n";
2592             final Map<String, Long> lastTimeMap = Maps.newHashMap();
2593             final PackageManager pm = mContext.getPackageManager();
2594             for (int i = 0; i < N; i++) {
2595                 SyncStorageEngine.SyncHistoryItem item = items.get(i);
2596                 SyncStorageEngine.AuthorityInfo authorityInfo
2597                         = mSyncStorageEngine.getAuthority(item.authorityId);
2598                 final String authorityName;
2599                 final String accountKey;
2600                 if (authorityInfo != null) {
2601                     authorityName = authorityInfo.target.provider;
2602                     accountKey = authorityInfo.target.account.name + "/"
2603                             + authorityInfo.target.account.type
2604                             + " u" + authorityInfo.target.userId;
2605                 } else {
2606                     authorityName = "Unknown";
2607                     accountKey = "Unknown";
2608                 }
2609                 final long elapsedTime = item.elapsedTime;
2610                 final long eventTime = item.eventTime;
2611 
2612                 final String key = authorityName + "/" + accountKey;
2613                 final Long lastEventTime = lastTimeMap.get(key);
2614                 final String diffString;
2615                 if (lastEventTime == null) {
2616                     diffString = "";
2617                 } else {
2618                     final long diff = (lastEventTime - eventTime) / 1000;
2619                     if (diff < 60) {
2620                         diffString = String.valueOf(diff);
2621                     } else if (diff < 3600) {
2622                         diffString = String.format("%02d:%02d", diff / 60, diff % 60);
2623                     } else {
2624                         final long sec = diff % 3600;
2625                         diffString = String.format("%02d:%02d:%02d",
2626                                 diff / 3600, sec / 60, sec % 60);
2627                     }
2628                 }
2629                 lastTimeMap.put(key, eventTime);
2630 
2631                 pw.printf("  #%-3d: %s %8s  %5.1fs  %8s",
2632                         i + 1,
2633                         formatTime(eventTime),
2634                         SyncStorageEngine.SOURCES[item.source],
2635                         ((float) elapsedTime) / 1000,
2636                         diffString);
2637                 pw.printf(format, accountKey, authorityName,
2638                         SyncOperation.reasonToString(pm, item.reason));
2639 
2640                 if (item.event != SyncStorageEngine.EVENT_STOP
2641                         || item.upstreamActivity != 0
2642                         || item.downstreamActivity != 0) {
2643                     pw.printf("    event=%d upstreamActivity=%d downstreamActivity=%d\n",
2644                             item.event,
2645                             item.upstreamActivity,
2646                             item.downstreamActivity);
2647                 }
2648                 if (item.mesg != null
2649                         && !SyncStorageEngine.MESG_SUCCESS.equals(item.mesg)) {
2650                     pw.printf("    mesg=%s\n", item.mesg);
2651                 }
2652             }
2653             pw.println();
2654             pw.println("Recent Sync History Extras");
2655             pw.println("(SERVER is now split up to FEED and OTHER)");
2656             for (int i = 0; i < N; i++) {
2657                 final SyncStorageEngine.SyncHistoryItem item = items.get(i);
2658                 final Bundle extras = item.extras;
2659                 if (extras == null || extras.size() == 0) {
2660                     continue;
2661                 }
2662                 final SyncStorageEngine.AuthorityInfo authorityInfo
2663                         = mSyncStorageEngine.getAuthority(item.authorityId);
2664                 final String authorityName;
2665                 final String accountKey;
2666                 if (authorityInfo != null) {
2667                     authorityName = authorityInfo.target.provider;
2668                     accountKey = authorityInfo.target.account.name + "/"
2669                             + authorityInfo.target.account.type
2670                             + " u" + authorityInfo.target.userId;
2671                 } else {
2672                     authorityName = "Unknown";
2673                     accountKey = "Unknown";
2674                 }
2675                 final long eventTime = item.eventTime;
2676 
2677                 pw.printf("  #%-3d: %s %8s ",
2678                         i + 1,
2679                         formatTime(eventTime),
2680                         SyncStorageEngine.SOURCES[item.source]);
2681 
2682                 pw.printf(format, accountKey, authorityName, extras);
2683             }
2684         }
2685     }
2686 
dumpDayStatistics(PrintWriter pw)2687     private void dumpDayStatistics(PrintWriter pw) {
2688         SyncStorageEngine.DayStats dses[] = mSyncStorageEngine.getDayStatistics();
2689         if (dses != null && dses[0] != null) {
2690             pw.println();
2691             pw.println("Sync Statistics");
2692             pw.print("  Today:  "); dumpDayStatistic(pw, dses[0]);
2693             int today = dses[0].day;
2694             int i;
2695             SyncStorageEngine.DayStats ds;
2696 
2697             // Print each day in the current week.
2698             for (i=1; i<=6 && i < dses.length; i++) {
2699                 ds = dses[i];
2700                 if (ds == null) break;
2701                 int delta = today-ds.day;
2702                 if (delta > 6) break;
2703 
2704                 pw.print("  Day-"); pw.print(delta); pw.print(":  ");
2705                 dumpDayStatistic(pw, ds);
2706             }
2707 
2708             // Aggregate all following days into weeks and print totals.
2709             int weekDay = today;
2710             while (i < dses.length) {
2711                 SyncStorageEngine.DayStats aggr = null;
2712                 weekDay -= 7;
2713                 while (i < dses.length) {
2714                     ds = dses[i];
2715                     if (ds == null) {
2716                         i = dses.length;
2717                         break;
2718                     }
2719                     int delta = weekDay-ds.day;
2720                     if (delta > 6) break;
2721                     i++;
2722 
2723                     if (aggr == null) {
2724                         aggr = new SyncStorageEngine.DayStats(weekDay);
2725                     }
2726                     aggr.successCount += ds.successCount;
2727                     aggr.successTime += ds.successTime;
2728                     aggr.failureCount += ds.failureCount;
2729                     aggr.failureTime += ds.failureTime;
2730                 }
2731                 if (aggr != null) {
2732                     pw.print("  Week-"); pw.print((today-weekDay)/7); pw.print(": ");
2733                     dumpDayStatistic(pw, aggr);
2734                 }
2735             }
2736         }
2737     }
2738 
dumpSyncAdapters(IndentingPrintWriter pw)2739     private void dumpSyncAdapters(IndentingPrintWriter pw) {
2740         pw.println();
2741         final List<UserInfo> users = getAllUsers();
2742         if (users != null) {
2743             for (UserInfo user : users) {
2744                 pw.println("Sync adapters for " + user + ":");
2745                 pw.increaseIndent();
2746                 for (RegisteredServicesCache.ServiceInfo<?> info :
2747                         mSyncAdapters.getAllServices(user.id)) {
2748                     pw.println(info);
2749                 }
2750                 pw.decreaseIndent();
2751                 pw.println();
2752             }
2753         }
2754     }
2755 
2756     private static class AuthoritySyncStats {
2757         String name;
2758         long elapsedTime;
2759         int times;
2760         Map<String, AccountSyncStats> accountMap = Maps.newHashMap();
2761 
AuthoritySyncStats(String name)2762         private AuthoritySyncStats(String name) {
2763             this.name = name;
2764         }
2765     }
2766 
2767     private static class AccountSyncStats {
2768         String name;
2769         long elapsedTime;
2770         int times;
2771 
AccountSyncStats(String name)2772         private AccountSyncStats(String name) {
2773             this.name = name;
2774         }
2775     }
2776 
2777     interface OnReadyCallback {
onReady()2778         void onReady();
2779     }
2780 
sendOnUnsyncableAccount(@onNull Context context, @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo, @UserIdInt int userId, @NonNull OnReadyCallback onReadyCallback)2781     static void sendOnUnsyncableAccount(@NonNull Context context,
2782             @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo,
2783             @UserIdInt int userId, @NonNull OnReadyCallback onReadyCallback) {
2784         OnUnsyncableAccountCheck connection = new OnUnsyncableAccountCheck(syncAdapterInfo,
2785                 onReadyCallback);
2786 
2787         boolean isBound = context.bindServiceAsUser(
2788                 getAdapterBindIntent(context, syncAdapterInfo.componentName, userId),
2789                 connection, SYNC_ADAPTER_CONNECTION_FLAGS, UserHandle.of(userId));
2790 
2791         if (isBound) {
2792             // Unbind after SERVICE_BOUND_TIME_MILLIS to not leak the connection.
2793             (new Handler(Looper.getMainLooper())).postDelayed(
2794                     () -> context.unbindService(connection),
2795                     OnUnsyncableAccountCheck.SERVICE_BOUND_TIME_MILLIS);
2796         } else {
2797                 /*
2798                  * The default implementation of adapter.onUnsyncableAccount returns true. Hence if
2799                  * there the service cannot be bound, assume the default behavior.
2800                  */
2801             connection.onReady();
2802         }
2803     }
2804 
2805 
2806     /**
2807      * Helper class for calling ISyncAdapter.onUnsyncableAccountDone.
2808      *
2809      * If this returns {@code true} the onReadyCallback is called. Otherwise nothing happens.
2810      */
2811     private static class OnUnsyncableAccountCheck implements ServiceConnection {
2812         static final long SERVICE_BOUND_TIME_MILLIS = 5000;
2813 
2814         private final @NonNull OnReadyCallback mOnReadyCallback;
2815         private final @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType>
2816                 mSyncAdapterInfo;
2817 
OnUnsyncableAccountCheck( @onNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo, @NonNull OnReadyCallback onReadyCallback)2818         OnUnsyncableAccountCheck(
2819                 @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo,
2820                 @NonNull OnReadyCallback onReadyCallback) {
2821             mSyncAdapterInfo = syncAdapterInfo;
2822             mOnReadyCallback = onReadyCallback;
2823         }
2824 
onReady()2825         private void onReady() {
2826             final long identity = Binder.clearCallingIdentity();
2827             try {
2828                 mOnReadyCallback.onReady();
2829             } finally {
2830                 Binder.restoreCallingIdentity(identity);
2831             }
2832         }
2833 
2834         @Override
onServiceConnected(ComponentName name, IBinder service)2835         public void onServiceConnected(ComponentName name, IBinder service) {
2836             final ISyncAdapter adapter = ISyncAdapter.Stub.asInterface(service);
2837 
2838             try {
2839                 adapter.onUnsyncableAccount(new ISyncAdapterUnsyncableAccountCallback.Stub() {
2840                     @Override
2841                     public void onUnsyncableAccountDone(boolean isReady) {
2842                         if (isReady) {
2843                             onReady();
2844                         }
2845                     }
2846                 });
2847             } catch (RemoteException e) {
2848                 Slog.e(TAG, "Could not call onUnsyncableAccountDone " + mSyncAdapterInfo, e);
2849                 /*
2850                  * The default implementation of adapter.onUnsyncableAccount returns true. Hence if
2851                  * there is a crash in the implementation, assume the default behavior.
2852                  */
2853                 onReady();
2854             }
2855         }
2856 
2857         @Override
onServiceDisconnected(ComponentName name)2858         public void onServiceDisconnected(ComponentName name) {
2859             // Wait until the service connects again
2860         }
2861     }
2862 
2863     /**
2864      * A helper object to keep track of the time we have spent syncing since the last boot
2865      */
2866     private class SyncTimeTracker {
2867         /** True if a sync was in progress on the most recent call to update() */
2868         boolean mLastWasSyncing = false;
2869         /** Used to track when lastWasSyncing was last set */
2870         long mWhenSyncStarted = 0;
2871         /** The cumulative time we have spent syncing */
2872         private long mTimeSpentSyncing;
2873 
2874         /** Call to let the tracker know that the sync state may have changed */
update()2875         public synchronized void update() {
2876             final boolean isSyncInProgress = !mActiveSyncContexts.isEmpty();
2877             if (isSyncInProgress == mLastWasSyncing) return;
2878             final long now = SystemClock.elapsedRealtime();
2879             if (isSyncInProgress) {
2880                 mWhenSyncStarted = now;
2881             } else {
2882                 mTimeSpentSyncing += now - mWhenSyncStarted;
2883             }
2884             mLastWasSyncing = isSyncInProgress;
2885         }
2886 
2887         /** Get how long we have been syncing, in ms */
timeSpentSyncing()2888         public synchronized long timeSpentSyncing() {
2889             if (!mLastWasSyncing) return mTimeSpentSyncing;
2890 
2891             final long now = SystemClock.elapsedRealtime();
2892             return mTimeSpentSyncing + (now - mWhenSyncStarted);
2893         }
2894     }
2895 
2896     class ServiceConnectionData {
2897         public final ActiveSyncContext activeSyncContext;
2898         public final IBinder adapter;
2899 
ServiceConnectionData(ActiveSyncContext activeSyncContext, IBinder adapter)2900         ServiceConnectionData(ActiveSyncContext activeSyncContext, IBinder adapter) {
2901             this.activeSyncContext = activeSyncContext;
2902             this.adapter = adapter;
2903         }
2904     }
2905 
2906     @Nullable
getInstance()2907     private static SyncManager getInstance() {
2908         synchronized (SyncManager.class) {
2909             if (sInstance == null) {
2910                 Slog.wtf(TAG, "sInstance == null"); // Maybe called too early?
2911             }
2912             return sInstance;
2913         }
2914     }
2915 
2916     /**
2917      * @return whether the device is ready to run sync jobs for a given user.
2918      */
readyToSync(int userId)2919     public static boolean readyToSync(int userId) {
2920         final SyncManager instance = getInstance();
2921         return (instance != null) && SyncJobService.isReady()
2922                 && instance.mProvisioned && instance.isUserUnlocked(userId);
2923     }
2924 
sendMessage(Message message)2925     public static void sendMessage(Message message) {
2926         final SyncManager instance = getInstance();
2927         if (instance != null) {
2928             instance.mSyncHandler.sendMessage(message);
2929         }
2930     }
2931 
2932     /**
2933      * Handles SyncOperation Messages that are posted to the associated
2934      * HandlerThread.
2935      */
2936     class SyncHandler extends Handler {
2937         // Messages that can be sent on mHandler.
2938         private static final int MESSAGE_SYNC_FINISHED = 1;
2939         private static final int MESSAGE_SERVICE_CONNECTED = 4;
2940         private static final int MESSAGE_SERVICE_DISCONNECTED = 5;
2941         private static final int MESSAGE_CANCEL = 6;
2942         static final int MESSAGE_START_SYNC = 10;
2943         static final int MESSAGE_STOP_SYNC = 11;
2944         static final int MESSAGE_SCHEDULE_SYNC = 12;
2945         static final int MESSAGE_UPDATE_PERIODIC_SYNC = 13;
2946         static final int MESSAGE_REMOVE_PERIODIC_SYNC = 14;
2947 
2948         /**
2949          * Posted periodically to monitor network process for long-running syncs.
2950          * obj: {@link com.android.server.content.SyncManager.ActiveSyncContext}
2951          */
2952         private static final int MESSAGE_MONITOR_SYNC = 8;
2953         private static final int MESSAGE_ACCOUNTS_UPDATED = 9;
2954 
2955         public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker();
2956         private final HashMap<String, PowerManager.WakeLock> mWakeLocks = Maps.newHashMap();
2957 
SyncHandler(Looper looper)2958         public SyncHandler(Looper looper) {
2959             super(looper);
2960         }
2961 
handleMessage(Message msg)2962         public void handleMessage(Message msg) {
2963             // TODO Do we really need this wake lock?? If we actually needed it, this is probably
2964             // not the best place to acquire the lock -- it's probably too late, because the device
2965             // could have gone to sleep before we reach here.
2966             mSyncManagerWakeLock.acquire();
2967             try {
2968                 handleSyncMessage(msg);
2969             } finally {
2970                 mSyncManagerWakeLock.release();
2971             }
2972         }
2973 
handleSyncMessage(Message msg)2974         private void handleSyncMessage(Message msg) {
2975             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
2976 
2977             try {
2978                 mDataConnectionIsConnected = readDataConnectionState();
2979                 switch (msg.what) {
2980                     case MESSAGE_ACCOUNTS_UPDATED:
2981                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2982                             Slog.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED");
2983                         }
2984                         EndPoint targets = (EndPoint) msg.obj;
2985                         updateRunningAccountsH(targets);
2986                         break;
2987                     case MESSAGE_SCHEDULE_SYNC:
2988                         ScheduleSyncMessagePayload syncPayload =
2989                                 (ScheduleSyncMessagePayload) msg.obj;
2990                         SyncOperation op = syncPayload.syncOperation;
2991                         scheduleSyncOperationH(op, syncPayload.minDelayMillis);
2992                         break;
2993 
2994                     case MESSAGE_START_SYNC:
2995                         op = (SyncOperation) msg.obj;
2996                         startSyncH(op);
2997                         break;
2998 
2999                     case MESSAGE_STOP_SYNC:
3000                         op = (SyncOperation) msg.obj;
3001                         if (isLoggable) {
3002                             Slog.v(TAG, "Stop sync received.");
3003                         }
3004                         ActiveSyncContext asc = findActiveSyncContextH(op.jobId);
3005                         if (asc != null) {
3006                             runSyncFinishedOrCanceledH(null /* no result */, asc);
3007                             boolean reschedule = msg.arg1 != 0;
3008                             boolean applyBackoff = msg.arg2 != 0;
3009                             if (isLoggable) {
3010                                 Slog.v(TAG, "Stopping sync. Reschedule: " + reschedule
3011                                         + "Backoff: " + applyBackoff);
3012                             }
3013                             if (applyBackoff) {
3014                                 increaseBackoffSetting(op.target);
3015                             }
3016                             if (reschedule) {
3017                                 deferStoppedSyncH(op, 0);
3018                             }
3019                         }
3020                         break;
3021 
3022                     case MESSAGE_UPDATE_PERIODIC_SYNC:
3023                         UpdatePeriodicSyncMessagePayload data =
3024                                 (UpdatePeriodicSyncMessagePayload) msg.obj;
3025                         updateOrAddPeriodicSyncH(data.target, data.pollFrequency,
3026                                 data.flex, data.extras);
3027                         break;
3028                     case MESSAGE_REMOVE_PERIODIC_SYNC:
3029                         Pair<EndPoint, String> args = (Pair<EndPoint, String>) (msg.obj);
3030                         removePeriodicSyncH(args.first, msg.getData(), args.second);
3031                         break;
3032 
3033                     case SyncHandler.MESSAGE_CANCEL:
3034                         SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj;
3035                         Bundle extras = msg.peekData();
3036                         if (isLoggable) {
3037                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_CANCEL: "
3038                                     + endpoint + " bundle: " + extras);
3039                         }
3040                         cancelActiveSyncH(endpoint, extras, "MESSAGE_CANCEL");
3041                         break;
3042 
3043                     case SyncHandler.MESSAGE_SYNC_FINISHED:
3044                         SyncFinishedOrCancelledMessagePayload payload =
3045                                 (SyncFinishedOrCancelledMessagePayload) msg.obj;
3046                         if (!isSyncStillActiveH(payload.activeSyncContext)) {
3047                             if (isLoggable) {
3048                                 Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
3049                                         + "sync is no longer active: "
3050                                         + payload.activeSyncContext);
3051                             }
3052                             break;
3053                         }
3054                         if (isLoggable) {
3055                             Slog.v(TAG, "syncFinished" + payload.activeSyncContext.mSyncOperation);
3056                         }
3057                         SyncJobService.callJobFinished(
3058                                 payload.activeSyncContext.mSyncOperation.jobId, false,
3059                                 "sync finished");
3060                         runSyncFinishedOrCanceledH(payload.syncResult,
3061                                 payload.activeSyncContext);
3062                         break;
3063 
3064                     case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
3065                         ServiceConnectionData msgData = (ServiceConnectionData) msg.obj;
3066                         if (isLoggable) {
3067                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
3068                                     + msgData.activeSyncContext);
3069                         }
3070                         // Check that this isn't an old message.
3071                         if (isSyncStillActiveH(msgData.activeSyncContext)) {
3072                             runBoundToAdapterH(
3073                                     msgData.activeSyncContext,
3074                                     msgData.adapter);
3075                         }
3076                         break;
3077                     }
3078 
3079                     case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
3080                         final ActiveSyncContext currentSyncContext =
3081                                 ((ServiceConnectionData) msg.obj).activeSyncContext;
3082                         if (isLoggable) {
3083                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
3084                                     + currentSyncContext);
3085                         }
3086                         // Check that this isn't an old message.
3087                         if (isSyncStillActiveH(currentSyncContext)) {
3088                             // cancel the sync if we have a syncadapter, which means one is
3089                             // outstanding
3090                             try {
3091                                 if (currentSyncContext.mSyncAdapter != null) {
3092                                     mLogger.log("Calling cancelSync for SERVICE_DISCONNECTED ",
3093                                             currentSyncContext,
3094                                             " adapter=", currentSyncContext.mSyncAdapter);
3095                                     currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext);
3096                                     mLogger.log("Canceled");
3097                                 }
3098                             } catch (RemoteException e) {
3099                                 mLogger.log("RemoteException ", Log.getStackTraceString(e));
3100                                 // We don't need to retry this in this case.
3101                             }
3102 
3103                             // Pretend that the sync failed with an IOException,
3104                             // which is a soft error.
3105                             SyncResult syncResult = new SyncResult();
3106                             syncResult.stats.numIoExceptions++;
3107                             SyncJobService.callJobFinished(
3108                                     currentSyncContext.mSyncOperation.jobId, false,
3109                                     "service disconnected");
3110                             runSyncFinishedOrCanceledH(syncResult, currentSyncContext);
3111                         }
3112                         break;
3113                     }
3114 
3115                     case SyncHandler.MESSAGE_MONITOR_SYNC:
3116                         ActiveSyncContext monitoredSyncContext = (ActiveSyncContext) msg.obj;
3117                         if (isLoggable) {
3118                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_MONITOR_SYNC: " +
3119                                     monitoredSyncContext.mSyncOperation.target);
3120                         }
3121 
3122                         if (isSyncNotUsingNetworkH(monitoredSyncContext)) {
3123                             Log.w(TAG, String.format(
3124                                     "Detected sync making no progress for %s. cancelling.",
3125                                     logSafe(monitoredSyncContext)));
3126                             SyncJobService.callJobFinished(
3127                                     monitoredSyncContext.mSyncOperation.jobId, false,
3128                                     "no network activity");
3129                             runSyncFinishedOrCanceledH(
3130                                     null /* cancel => no result */, monitoredSyncContext);
3131                         } else {
3132                             // Repost message to check again.
3133                             postMonitorSyncProgressMessage(monitoredSyncContext);
3134                         }
3135                         break;
3136 
3137                 }
3138             } finally {
3139                 mSyncTimeTracker.update();
3140             }
3141         }
3142 
getSyncWakeLock(SyncOperation operation)3143         private PowerManager.WakeLock getSyncWakeLock(SyncOperation operation) {
3144             final String wakeLockKey = operation.wakeLockName();
3145             PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey);
3146             if (wakeLock == null) {
3147                 final String name = SYNC_WAKE_LOCK_PREFIX + wakeLockKey;
3148                 wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
3149                 wakeLock.setReferenceCounted(false);
3150                 mWakeLocks.put(wakeLockKey, wakeLock);
3151             }
3152             return wakeLock;
3153         }
3154 
3155         /**
3156          * Defer the specified SyncOperation by rescheduling it on the JobScheduler with some
3157          * delay. This is equivalent to a failure. If this is a periodic sync, a delayed one-off
3158          * sync will be scheduled.
3159          */
deferSyncH(SyncOperation op, long delay, String why)3160         private void deferSyncH(SyncOperation op, long delay, String why) {
3161             mLogger.log("deferSyncH() ", (op.isPeriodic ? "periodic " : ""),
3162                     "sync.  op=", op, " delay=", delay, " why=", why);
3163             SyncJobService.callJobFinished(op.jobId, false, why);
3164             if (op.isPeriodic) {
3165                 scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay);
3166             } else {
3167                 // mSyncJobService.callJobFinished is async, so cancel the job to ensure we don't
3168                 // find this job in the pending jobs list while looking for duplicates
3169                 // before scheduling it at a later time.
3170                 cancelJob(op, "deferSyncH()");
3171                 scheduleSyncOperationH(op, delay);
3172             }
3173         }
3174 
3175         /* Same as deferSyncH, but assumes that job is no longer running on JobScheduler. */
deferStoppedSyncH(SyncOperation op, long delay)3176         private void deferStoppedSyncH(SyncOperation op, long delay) {
3177             if (op.isPeriodic) {
3178                 scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay);
3179             } else {
3180                 scheduleSyncOperationH(op, delay);
3181             }
3182         }
3183 
3184         /**
3185          * Cancel an active sync and reschedule it on the JobScheduler with some delay.
3186          */
deferActiveSyncH(ActiveSyncContext asc, String why)3187         private void deferActiveSyncH(ActiveSyncContext asc, String why) {
3188             SyncOperation op = asc.mSyncOperation;
3189             runSyncFinishedOrCanceledH(null, asc);
3190             deferSyncH(op, SYNC_DELAY_ON_CONFLICT, why);
3191         }
3192 
startSyncH(SyncOperation op)3193         private void startSyncH(SyncOperation op) {
3194             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
3195             if (isLoggable) Slog.v(TAG, op.toString());
3196 
3197             // At this point, we know the device has been connected to the server, so
3198             // assume the clock is correct.
3199             mSyncStorageEngine.setClockValid();
3200 
3201             SyncJobService.markSyncStarted(op.jobId);
3202 
3203             if (op.isPeriodic) {
3204                 // Don't allow this periodic to run if a previous instance failed and is currently
3205                 // scheduled according to some backoff criteria.
3206                 List<SyncOperation> ops = getAllPendingSyncs();
3207                 for (SyncOperation syncOperation: ops) {
3208                     if (syncOperation.sourcePeriodicId == op.jobId) {
3209                         SyncJobService.callJobFinished(op.jobId, false,
3210                                 "periodic sync, pending");
3211                         return;
3212                     }
3213                 }
3214                 // Don't allow this periodic to run if a previous instance failed and is currently
3215                 // executing according to some backoff criteria.
3216                 for (ActiveSyncContext asc: mActiveSyncContexts) {
3217                     if (asc.mSyncOperation.sourcePeriodicId == op.jobId) {
3218                         SyncJobService.callJobFinished(op.jobId, false,
3219                                 "periodic sync, already running");
3220                         return;
3221                     }
3222                 }
3223                 // Check for adapter delays.
3224                 if (isAdapterDelayed(op.target)) {
3225                     deferSyncH(op, 0 /* No minimum delay */, "backing off");
3226                     return;
3227                 }
3228             }
3229 
3230             // Check for conflicting syncs.
3231             for (ActiveSyncContext asc: mActiveSyncContexts) {
3232                 if (asc.mSyncOperation.isConflict(op)) {
3233                     // If the provided SyncOperation conflicts with a running one, the lower
3234                     // priority sync is pre-empted.
3235                     if (asc.mSyncOperation.getJobBias() >= op.getJobBias()) {
3236                         if (isLoggable) {
3237                             Slog.v(TAG, "Rescheduling sync due to conflict " + op.toString());
3238                         }
3239                         deferSyncH(op, SYNC_DELAY_ON_CONFLICT, "delay on conflict");
3240                         return;
3241                     } else {
3242                         if (isLoggable) {
3243                             Slog.v(TAG, "Pushing back running sync due to a higher priority sync");
3244                         }
3245                         deferActiveSyncH(asc, "preempted");
3246                         break;
3247                     }
3248                 }
3249             }
3250 
3251             final int syncOpState = computeSyncOpState(op);
3252             if (syncOpState != SYNC_OP_STATE_VALID) {
3253                 SyncJobService.callJobFinished(op.jobId, false,
3254                         "invalid op state: " + syncOpState);
3255                 return;
3256             }
3257 
3258             if (!dispatchSyncOperation(op)) {
3259                 SyncJobService.callJobFinished(op.jobId, false, "dispatchSyncOperation() failed");
3260             }
3261 
3262             setAuthorityPendingState(op.target);
3263         }
3264 
findActiveSyncContextH(int jobId)3265         private ActiveSyncContext findActiveSyncContextH(int jobId) {
3266             for (ActiveSyncContext asc: mActiveSyncContexts) {
3267                 SyncOperation op = asc.mSyncOperation;
3268                 if (op != null && op.jobId == jobId) {
3269                     return asc;
3270                 }
3271             }
3272             return null;
3273         }
3274 
updateRunningAccountsH(EndPoint syncTargets)3275         private void updateRunningAccountsH(EndPoint syncTargets) {
3276             synchronized (mAccountsLock) {
3277                 AccountAndUser[] oldAccounts = mRunningAccounts;
3278                 mRunningAccounts =
3279                         AccountManagerService.getSingleton().getRunningAccountsForSystem();
3280                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3281                     Slog.v(TAG, "Accounts list: ");
3282                     for (AccountAndUser acc : mRunningAccounts) {
3283                         Slog.v(TAG, acc.toString());
3284                     }
3285                 }
3286                 if (mLogger.enabled()) {
3287                     mLogger.log("updateRunningAccountsH: ", Arrays.toString(mRunningAccounts));
3288                 }
3289                 removeStaleAccounts();
3290 
3291                 AccountAndUser[] accounts = mRunningAccounts;
3292                 for (int i = 0, size = mActiveSyncContexts.size(); i < size; i++) {
3293                     ActiveSyncContext currentSyncContext = mActiveSyncContexts.get(i);
3294                     if (!containsAccountAndUser(accounts,
3295                             currentSyncContext.mSyncOperation.target.account,
3296                             currentSyncContext.mSyncOperation.target.userId)) {
3297                         Log.d(TAG, "canceling sync since the account is no longer running");
3298                         sendSyncFinishedOrCanceledMessage(currentSyncContext,
3299                                 null /* no result since this is a cancel */);
3300                     }
3301                 }
3302 
3303                 if (syncTargets != null) {
3304                     // On account add, check if there are any settings to be restored.
3305                     for (int i = 0, length = mRunningAccounts.length; i < length; i++) {
3306                         AccountAndUser aau = mRunningAccounts[i];
3307                         if (!containsAccountAndUser(oldAccounts, aau.account, aau.userId)) {
3308                             if (Log.isLoggable(TAG, Log.DEBUG)) {
3309                                 Log.d(TAG, "Account " + aau.account
3310                                         + " added, checking sync restore data");
3311                             }
3312                             AccountSyncSettingsBackupHelper.accountAdded(mContext,
3313                                     syncTargets.userId);
3314                             break;
3315                         }
3316                     }
3317                 }
3318             }
3319 
3320             // Cancel all jobs from non-existent accounts.
3321             AccountAndUser[] allAccounts =
3322                     AccountManagerService.getSingleton().getAllAccountsForSystemProcess();
3323             List<SyncOperation> ops = getAllPendingSyncs();
3324             for (int i = 0, opsSize = ops.size(); i < opsSize; i++) {
3325                 SyncOperation op = ops.get(i);
3326                 if (!containsAccountAndUser(allAccounts, op.target.account, op.target.userId)) {
3327                     mLogger.log("canceling: ", op);
3328                     cancelJob(op, "updateRunningAccountsH()");
3329                 }
3330             }
3331 
3332             if (syncTargets != null) {
3333                 scheduleSync(syncTargets.account, syncTargets.userId,
3334                         SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider,
3335                         null, AuthorityInfo.NOT_INITIALIZED,
3336                         ContentResolver.SYNC_EXEMPTION_NONE, Process.myUid(), -4, null);
3337             }
3338         }
3339 
3340         /**
3341          * The given SyncOperation will be removed and a new one scheduled in its place if
3342          * an updated period or flex is specified.
3343          * @param syncOperation SyncOperation whose period and flex is to be updated.
3344          * @param pollFrequencyMillis new period in milliseconds.
3345          * @param flexMillis new flex time in milliseconds.
3346          */
maybeUpdateSyncPeriodH(SyncOperation syncOperation, long pollFrequencyMillis, long flexMillis)3347         private void maybeUpdateSyncPeriodH(SyncOperation syncOperation, long pollFrequencyMillis,
3348                 long flexMillis) {
3349             if (!(pollFrequencyMillis == syncOperation.periodMillis
3350                     && flexMillis == syncOperation.flexMillis)) {
3351                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3352                     Slog.v(TAG, "updating period " + syncOperation + " to " + pollFrequencyMillis
3353                             + " and flex to " + flexMillis);
3354                 }
3355                 SyncOperation newOp = new SyncOperation(syncOperation, pollFrequencyMillis,
3356                         flexMillis);
3357                 newOp.jobId = syncOperation.jobId;
3358                 scheduleSyncOperationH(newOp);
3359             }
3360         }
3361 
updateOrAddPeriodicSyncH(EndPoint target, long pollFrequency, long flex, Bundle extras)3362         private void updateOrAddPeriodicSyncH(EndPoint target, long pollFrequency, long flex,
3363                 Bundle extras) {
3364             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
3365             verifyJobScheduler();  // Will fill in mScheduledSyncs cache if it is not already filled.
3366             final long pollFrequencyMillis = pollFrequency * 1000L;
3367             final long flexMillis = flex * 1000L;
3368             if (isLoggable) {
3369                 Slog.v(TAG, "Addition to periodic syncs requested: " + target
3370                         + " period: " + pollFrequency
3371                         + " flexMillis: " + flex
3372                         + " extras: " + extras.toString());
3373             }
3374             List<SyncOperation> ops = getAllPendingSyncs();
3375             for (SyncOperation op: ops) {
3376                 if (op.isPeriodic && op.target.matchesSpec(target)
3377                         && op.areExtrasEqual(extras, /*includeSyncSettings=*/ true)) {
3378                     maybeUpdateSyncPeriodH(op, pollFrequencyMillis, flexMillis);
3379                     return;
3380                 }
3381             }
3382 
3383             if (isLoggable) {
3384                 Slog.v(TAG, "Adding new periodic sync: " + target
3385                         + " period: " + pollFrequency
3386                         + " flexMillis: " + flex
3387                         + " extras: " + extras.toString());
3388             }
3389 
3390             final RegisteredServicesCache.ServiceInfo<SyncAdapterType>
3391                     syncAdapterInfo = mSyncAdapters.getServiceInfo(
3392                     SyncAdapterType.newKey(
3393                             target.provider, target.account.type),
3394                     target.userId);
3395             if (syncAdapterInfo == null) {
3396                 return;
3397             }
3398 
3399             SyncOperation op = new SyncOperation(target, syncAdapterInfo.uid,
3400                     syncAdapterInfo.componentName.getPackageName(), SyncOperation.REASON_PERIODIC,
3401                     SyncStorageEngine.SOURCE_PERIODIC, extras,
3402                     syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID,
3403                     pollFrequencyMillis, flexMillis, ContentResolver.SYNC_EXEMPTION_NONE);
3404 
3405             final int syncOpState = computeSyncOpState(op);
3406             if (syncOpState == SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS) {
3407                 String packageName = op.owningPackage;
3408                 final int userId = UserHandle.getUserId(op.owningUid);
3409                 // If the app did not run and has no account access, done
3410                 if (!wasPackageEverLaunched(packageName, userId)) {
3411                     return;
3412                 }
3413                 mLogger.log("requestAccountAccess for SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS");
3414                 mAccountManagerInternal.requestAccountAccess(op.target.account,
3415                         packageName, userId, new RemoteCallback((Bundle result) -> {
3416                             if (result != null
3417                                     && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
3418                                 updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
3419                             }
3420                         }
3421                         ));
3422                 return;
3423             }
3424             if (syncOpState != SYNC_OP_STATE_VALID) {
3425                 mLogger.log("syncOpState=", syncOpState);
3426                 return;
3427             }
3428 
3429             scheduleSyncOperationH(op);
3430             mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
3431                     op.owningPackage, target.userId);
3432         }
3433 
3434         /**
3435          * Remove this periodic sync operation and all one-off operations initiated by it.
3436          */
removePeriodicSyncInternalH(SyncOperation syncOperation, String why)3437         private void removePeriodicSyncInternalH(SyncOperation syncOperation, String why) {
3438             // Remove this periodic sync and all one-off syncs initiated by it.
3439             List<SyncOperation> ops = getAllPendingSyncs();
3440             for (SyncOperation op: ops) {
3441                 if (op.sourcePeriodicId == syncOperation.jobId || op.jobId == syncOperation.jobId) {
3442                     ActiveSyncContext asc = findActiveSyncContextH(syncOperation.jobId);
3443                     if (asc != null) {
3444                         SyncJobService.callJobFinished(syncOperation.jobId, false,
3445                                 "removePeriodicSyncInternalH");
3446                         runSyncFinishedOrCanceledH(null, asc);
3447                     }
3448                     mLogger.log("removePeriodicSyncInternalH-canceling: ", op);
3449                     cancelJob(op, why);
3450                 }
3451             }
3452         }
3453 
removePeriodicSyncH(EndPoint target, Bundle extras, String why)3454         private void removePeriodicSyncH(EndPoint target, Bundle extras, String why) {
3455             verifyJobScheduler();
3456             List<SyncOperation> ops = getAllPendingSyncs();
3457             for (SyncOperation op: ops) {
3458                 if (op.isPeriodic && op.target.matchesSpec(target)
3459                         && op.areExtrasEqual(extras, /*includeSyncSettings=*/ true)) {
3460                     removePeriodicSyncInternalH(op, why);
3461                 }
3462             }
3463         }
3464 
isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext)3465         private boolean isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext) {
3466             final long bytesTransferredCurrent =
3467                     getTotalBytesTransferredByUid(activeSyncContext.mSyncAdapterUid);
3468             final long deltaBytesTransferred =
3469                     bytesTransferredCurrent - activeSyncContext.mBytesTransferredAtLastPoll;
3470 
3471             if (Log.isLoggable(TAG, Log.DEBUG)) {
3472                 // Bytes transferred
3473                 long remainder = deltaBytesTransferred;
3474                 long mb = remainder / (1024 * 1024);
3475                 remainder %= 1024 * 1024;
3476                 long kb = remainder / 1024;
3477                 remainder %= 1024;
3478                 long b = remainder;
3479                 Log.d(TAG, String.format(
3480                         "Time since last update: %ds. Delta transferred: %dMBs,%dKBs,%dBs",
3481                         (SystemClock.elapsedRealtime()
3482                                 - activeSyncContext.mLastPolledTimeElapsed)/1000,
3483                         mb, kb, b)
3484                 );
3485             }
3486             return (deltaBytesTransferred <= SYNC_MONITOR_PROGRESS_THRESHOLD_BYTES);
3487         }
3488 
3489         /**
3490          * Determine if a sync is no longer valid and should be dropped.
3491          */
computeSyncOpState(SyncOperation op)3492         private int computeSyncOpState(SyncOperation op) {
3493             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
3494             int state;
3495             final EndPoint target = op.target;
3496 
3497             // Drop the sync if the account of this operation no longer exists.
3498             synchronized (mAccountsLock) {
3499                 AccountAndUser[] accounts = mRunningAccounts;
3500                 if (!containsAccountAndUser(accounts, target.account, target.userId)) {
3501                     if (isLoggable) {
3502                         Slog.v(TAG, "    Dropping sync operation: account doesn't exist.");
3503                     }
3504                     logAccountError("SYNC_OP_STATE_INVALID: account doesn't exist.");
3505                     return SYNC_OP_STATE_INVALID_NO_ACCOUNT;
3506                 }
3507             }
3508             // Drop this sync request if it isn't syncable.
3509             state = computeSyncable(target.account, target.userId, target.provider, true);
3510             if (state == AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS) {
3511                 if (isLoggable) {
3512                     Slog.v(TAG, "    Dropping sync operation: "
3513                             + "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS");
3514                 }
3515                 logAccountError("SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS");
3516                 return SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS;
3517             }
3518             if (state == AuthorityInfo.NOT_SYNCABLE) {
3519                 if (isLoggable) {
3520                     Slog.v(TAG, "    Dropping sync operation: isSyncable == NOT_SYNCABLE");
3521                 }
3522                 logAccountError("SYNC_OP_STATE_INVALID: NOT_SYNCABLE");
3523                 return SYNC_OP_STATE_INVALID_NOT_SYNCABLE;
3524             }
3525 
3526             final boolean syncEnabled = mSyncStorageEngine.getMasterSyncAutomatically(target.userId)
3527                     && mSyncStorageEngine.getSyncAutomatically(target.account,
3528                             target.userId, target.provider);
3529 
3530             // We ignore system settings that specify the sync is invalid if:
3531             // 1) It's manual - we try it anyway. When/if it fails it will be rescheduled.
3532             //      or
3533             // 2) it's an initialisation sync - we just need to connect to it.
3534             final boolean ignoreSystemConfiguration = op.isIgnoreSettings() || (state < 0);
3535 
3536             // Sync not enabled.
3537             if (!syncEnabled && !ignoreSystemConfiguration) {
3538                 if (isLoggable) {
3539                     Slog.v(TAG, "    Dropping sync operation: disallowed by settings/network.");
3540                 }
3541                 logAccountError("SYNC_OP_STATE_INVALID: disallowed by settings/network");
3542                 return SYNC_OP_STATE_INVALID_SYNC_DISABLED;
3543             }
3544             return SYNC_OP_STATE_VALID;
3545         }
3546 
logAccountError(String message)3547         private void logAccountError(String message) {
3548             if (USE_WTF_FOR_ACCOUNT_ERROR) {
3549                 Slog.wtf(TAG, message);
3550             } else {
3551                 Slog.e(TAG, message);
3552             }
3553         }
3554 
dispatchSyncOperation(SyncOperation op)3555         private boolean dispatchSyncOperation(SyncOperation op) {
3556             if (Log.isLoggable(TAG, Log.VERBOSE)) {
3557                 Slog.v(TAG, "dispatchSyncOperation: we are going to sync " + op);
3558                 Slog.v(TAG, "num active syncs: " + mActiveSyncContexts.size());
3559                 for (ActiveSyncContext syncContext : mActiveSyncContexts) {
3560                     Slog.v(TAG, syncContext.toString());
3561                 }
3562             }
3563             if (op.isAppStandbyExempted()) {
3564                 final UsageStatsManagerInternal usmi = LocalServices.getService(
3565                         UsageStatsManagerInternal.class);
3566                 if (usmi != null) {
3567                     usmi.reportExemptedSyncStart(op.owningPackage,
3568                             UserHandle.getUserId(op.owningUid));
3569                 }
3570             }
3571 
3572             // Connect to the sync adapter.
3573             int targetUid;
3574             ComponentName targetComponent;
3575             final SyncStorageEngine.EndPoint info = op.target;
3576             SyncAdapterType syncAdapterType =
3577                     SyncAdapterType.newKey(info.provider, info.account.type);
3578             final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
3579             syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, info.userId);
3580             if (syncAdapterInfo == null) {
3581                 mLogger.log("dispatchSyncOperation() failed: no sync adapter info for ",
3582                         syncAdapterType);
3583                 Log.d(TAG, "can't find a sync adapter for " + syncAdapterType
3584                         + ", removing settings for it");
3585                 mSyncStorageEngine.removeAuthority(info);
3586                 return false;
3587             }
3588             targetUid = syncAdapterInfo.uid;
3589             targetComponent = syncAdapterInfo.componentName;
3590             ActiveSyncContext activeSyncContext =
3591                     new ActiveSyncContext(op, insertStartSyncEvent(op), targetUid);
3592             if (Log.isLoggable(TAG, Log.VERBOSE)) {
3593                 Slog.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext);
3594             }
3595 
3596             activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext);
3597             mActiveSyncContexts.add(activeSyncContext);
3598 
3599             // Post message to begin monitoring this sync's progress.
3600             postMonitorSyncProgressMessage(activeSyncContext);
3601 
3602             if (!activeSyncContext.bindToSyncAdapter(targetComponent, info.userId)) {
3603                 mLogger.log("dispatchSyncOperation() failed: bind failed. target: ",
3604                         targetComponent);
3605                 Slog.e(TAG, "Bind attempt failed - target: " + targetComponent);
3606                 closeActiveSyncContext(activeSyncContext);
3607                 return false;
3608             }
3609 
3610             return true;
3611         }
3612 
runBoundToAdapterH(final ActiveSyncContext activeSyncContext, IBinder syncAdapter)3613         private void runBoundToAdapterH(final ActiveSyncContext activeSyncContext,
3614                 IBinder syncAdapter) {
3615             final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
3616             try {
3617                 activeSyncContext.mIsLinkedToDeath = true;
3618                 syncAdapter.linkToDeath(activeSyncContext, 0);
3619 
3620                 if (mLogger.enabled()) {
3621                     mLogger.log("Sync start: account=" + syncOperation.target.account,
3622                             " authority=", syncOperation.target.provider,
3623                             " reason=", SyncOperation.reasonToString(null, syncOperation.reason),
3624                             " extras=", syncOperation.getExtrasAsString(),
3625                             " adapter=", activeSyncContext.mSyncAdapter);
3626                 }
3627 
3628                 activeSyncContext.mSyncAdapter = ISyncAdapter.Stub.asInterface(syncAdapter);
3629                 activeSyncContext.mSyncAdapter
3630                         .startSync(activeSyncContext, syncOperation.target.provider,
3631                                 syncOperation.target.account, syncOperation.getClonedExtras());
3632 
3633                 mLogger.log("Sync is running now...");
3634             } catch (RemoteException remoteExc) {
3635                 mLogger.log("Sync failed with RemoteException: ", remoteExc.toString());
3636                 Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc);
3637                 closeActiveSyncContext(activeSyncContext);
3638                 increaseBackoffSetting(syncOperation.target);
3639                 scheduleSyncOperationH(syncOperation);
3640             } catch (RuntimeException exc) {
3641                 mLogger.log("Sync failed with RuntimeException: ", exc.toString());
3642                 closeActiveSyncContext(activeSyncContext);
3643                 Slog.e(TAG, "Caught RuntimeException while starting the sync "
3644                         + logSafe(syncOperation), exc);
3645             }
3646         }
3647 
3648         /**
3649          * Cancel the sync for the provided target that matches the given bundle.
3650          * @param info Can have null fields to indicate all the active syncs for that field.
3651          * @param extras Can be null to indicate <strong>all</strong> syncs for the given endpoint.
3652          */
cancelActiveSyncH(SyncStorageEngine.EndPoint info, Bundle extras, String why)3653         private void cancelActiveSyncH(SyncStorageEngine.EndPoint info, Bundle extras,
3654                 String why) {
3655             ArrayList<ActiveSyncContext> activeSyncs =
3656                     new ArrayList<ActiveSyncContext>(mActiveSyncContexts);
3657             for (ActiveSyncContext activeSyncContext : activeSyncs) {
3658                 if (activeSyncContext != null) {
3659                     final SyncStorageEngine.EndPoint opInfo =
3660                             activeSyncContext.mSyncOperation.target;
3661                     if (!opInfo.matchesSpec(info)) {
3662                         continue;
3663                     }
3664                     if (extras != null &&
3665                             !activeSyncContext.mSyncOperation.areExtrasEqual(extras,
3666                                     /*includeSyncSettings=*/ false)) {
3667                         continue;
3668                     }
3669                     SyncJobService.callJobFinished(activeSyncContext.mSyncOperation.jobId, false,
3670                             why);
3671                     runSyncFinishedOrCanceledH(null /* cancel => no result */, activeSyncContext);
3672                 }
3673             }
3674         }
3675 
3676         /**
3677          * Should be called when a one-off instance of a periodic sync completes successfully.
3678          */
reschedulePeriodicSyncH(SyncOperation syncOperation)3679         private void reschedulePeriodicSyncH(SyncOperation syncOperation) {
3680             // Ensure that the periodic sync wasn't removed.
3681             SyncOperation periodicSync = null;
3682             List<SyncOperation> ops = getAllPendingSyncs();
3683             for (SyncOperation op: ops) {
3684                 if (op.isPeriodic && syncOperation.matchesPeriodicOperation(op)) {
3685                     periodicSync = op;
3686                     break;
3687                 }
3688             }
3689             if (periodicSync == null) {
3690                 return;
3691             }
3692             scheduleSyncOperationH(periodicSync);
3693         }
3694 
runSyncFinishedOrCanceledH(SyncResult syncResult, ActiveSyncContext activeSyncContext)3695         private void runSyncFinishedOrCanceledH(SyncResult syncResult,
3696                 ActiveSyncContext activeSyncContext) {
3697             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
3698 
3699             final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
3700             final SyncStorageEngine.EndPoint info = syncOperation.target;
3701 
3702             if (activeSyncContext.mIsLinkedToDeath) {
3703                 activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0);
3704                 activeSyncContext.mIsLinkedToDeath = false;
3705             }
3706             final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime;
3707             String historyMessage;
3708             int downstreamActivity;
3709             int upstreamActivity;
3710 
3711             mLogger.log("runSyncFinishedOrCanceledH() op=", syncOperation, " result=", syncResult);
3712 
3713             if (syncResult != null) {
3714                 if (isLoggable) {
3715                     Slog.v(TAG, "runSyncFinishedOrCanceled [finished]: "
3716                             + syncOperation + ", result " + syncResult);
3717                 }
3718 
3719                 // In the non-canceled case, close the active sync context before doing the rest
3720                 // of the stuff.
3721                 closeActiveSyncContext(activeSyncContext);
3722 
3723                 // Note this part is probably okay to do before closeActiveSyncContext()...
3724                 // But moved here to restore OC-dev's behavior.  See b/64597061.
3725                 if (!syncOperation.isPeriodic) {
3726                     cancelJob(syncOperation, "runSyncFinishedOrCanceledH()-finished");
3727                 }
3728 
3729                 if (!syncResult.hasError()) {
3730                     historyMessage = SyncStorageEngine.MESG_SUCCESS;
3731                     // TODO: set these correctly when the SyncResult is extended to include it
3732                     downstreamActivity = 0;
3733                     upstreamActivity = 0;
3734                     clearBackoffSetting(syncOperation.target, "sync success");
3735 
3736                     // If the operation completes successfully and it was scheduled due to
3737                     // a periodic operation failing, we reschedule the periodic operation to
3738                     // start from now.
3739                     if (syncOperation.isDerivedFromFailedPeriodicSync()) {
3740                         reschedulePeriodicSyncH(syncOperation);
3741                     }
3742                 } else {
3743                     Log.w(TAG, "failed sync operation "
3744                             + logSafe(syncOperation) + ", " + syncResult);
3745 
3746                     syncOperation.retries++;
3747                     if (syncOperation.retries > mConstants.getMaxRetriesWithAppStandbyExemption()) {
3748                         syncOperation.syncExemptionFlag = ContentResolver.SYNC_EXEMPTION_NONE;
3749                     }
3750 
3751                     // the operation failed so increase the backoff time
3752                     increaseBackoffSetting(syncOperation.target);
3753                     if (!syncOperation.isPeriodic) {
3754                         // reschedule the sync if so indicated by the syncResult
3755                         maybeRescheduleSync(syncResult, syncOperation);
3756                     } else {
3757                         // create a normal sync instance that will respect adapter backoffs
3758                         postScheduleSyncMessage(syncOperation.createOneTimeSyncOperation(),
3759                                 0 /* min delay */);
3760                     }
3761                     historyMessage = ContentResolver.syncErrorToString(
3762                             syncResultToErrorNumber(syncResult));
3763                     // TODO: set these correctly when the SyncResult is extended to include it
3764                     downstreamActivity = 0;
3765                     upstreamActivity = 0;
3766                 }
3767                 setDelayUntilTime(syncOperation.target, syncResult.delayUntil);
3768             } else {
3769                 if (isLoggable) {
3770                     Slog.v(TAG, "runSyncFinishedOrCanceled [canceled]: " + syncOperation);
3771                 }
3772 
3773                 if (!syncOperation.isPeriodic) {
3774                     cancelJob(syncOperation, "runSyncFinishedOrCanceledH()-canceled");
3775                 }
3776 
3777                 if (activeSyncContext.mSyncAdapter != null) {
3778                     try {
3779                         mLogger.log("Calling cancelSync for runSyncFinishedOrCanceled ",
3780                                 activeSyncContext, "  adapter=", activeSyncContext.mSyncAdapter);
3781                         activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext);
3782                         mLogger.log("Canceled");
3783                     } catch (RemoteException e) {
3784                         mLogger.log("RemoteException ", Log.getStackTraceString(e));
3785                         // we don't need to retry this in this case
3786                     }
3787                 }
3788                 historyMessage = SyncStorageEngine.MESG_CANCELED;
3789                 downstreamActivity = 0;
3790                 upstreamActivity = 0;
3791 
3792                 // In the cancel sync case, close it after calling cancelSync().
3793                 closeActiveSyncContext(activeSyncContext);
3794             }
3795 
3796             stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage,
3797                     upstreamActivity, downstreamActivity, elapsedTime);
3798             // Check for full-resync and schedule it after closing off the last sync.
3799             if (syncResult != null && syncResult.tooManyDeletions) {
3800                 installHandleTooManyDeletesNotification(info.account,
3801                         info.provider, syncResult.stats.numDeletes,
3802                         info.userId);
3803             } else {
3804                 mNotificationMgr.cancelAsUser(
3805                         Integer.toString(info.account.hashCode() ^ info.provider.hashCode()),
3806                         SystemMessage.NOTE_SYNC_ERROR,
3807                         new UserHandle(info.userId));
3808             }
3809             if (syncResult != null && syncResult.fullSyncRequested) {
3810                 scheduleSyncOperationH(
3811                         new SyncOperation(info.account, info.userId,
3812                                 syncOperation.owningUid, syncOperation.owningPackage,
3813                                 syncOperation.reason,
3814                                 syncOperation.syncSource, info.provider, new Bundle(),
3815                                 syncOperation.allowParallelSyncs,
3816                                 syncOperation.syncExemptionFlag));
3817             }
3818         }
3819 
closeActiveSyncContext(ActiveSyncContext activeSyncContext)3820         private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) {
3821             activeSyncContext.close();
3822             mActiveSyncContexts.remove(activeSyncContext);
3823             mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo,
3824                     activeSyncContext.mSyncOperation.target.userId);
3825 
3826             if (Log.isLoggable(TAG, Log.VERBOSE)) {
3827                 Slog.v(TAG, "removing all MESSAGE_MONITOR_SYNC & MESSAGE_SYNC_EXPIRED for "
3828                         + activeSyncContext.toString());
3829             }
3830             mSyncHandler.removeMessages(SyncHandler.MESSAGE_MONITOR_SYNC, activeSyncContext);
3831 
3832             mLogger.log("closeActiveSyncContext: ", activeSyncContext);
3833         }
3834 
3835         /**
3836          * Convert the error-containing SyncResult into the Sync.History error number. Since
3837          * the SyncResult may indicate multiple errors at once, this method just returns the
3838          * most "serious" error.
3839          * @param syncResult the SyncResult from which to read
3840          * @return the most "serious" error set in the SyncResult
3841          * @throws IllegalStateException if the SyncResult does not indicate any errors.
3842          *   If SyncResult.error() is true then it is safe to call this.
3843          */
syncResultToErrorNumber(SyncResult syncResult)3844         private int syncResultToErrorNumber(SyncResult syncResult) {
3845             if (syncResult.syncAlreadyInProgress)
3846                 return ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
3847             if (syncResult.stats.numAuthExceptions > 0)
3848                 return ContentResolver.SYNC_ERROR_AUTHENTICATION;
3849             if (syncResult.stats.numIoExceptions > 0)
3850                 return ContentResolver.SYNC_ERROR_IO;
3851             if (syncResult.stats.numParseExceptions > 0)
3852                 return ContentResolver.SYNC_ERROR_PARSE;
3853             if (syncResult.stats.numConflictDetectedExceptions > 0)
3854                 return ContentResolver.SYNC_ERROR_CONFLICT;
3855             if (syncResult.tooManyDeletions)
3856                 return ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS;
3857             if (syncResult.tooManyRetries)
3858                 return ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES;
3859             if (syncResult.databaseError)
3860                 return ContentResolver.SYNC_ERROR_INTERNAL;
3861             throw new IllegalStateException("we are not in an error state, " + syncResult);
3862         }
3863 
installHandleTooManyDeletesNotification(Account account, String authority, long numDeletes, int userId)3864         private void installHandleTooManyDeletesNotification(Account account, String authority,
3865                 long numDeletes, int userId) {
3866             if (mNotificationMgr == null) return;
3867 
3868             final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider(
3869                     authority, 0 /* flags */);
3870             if (providerInfo == null) {
3871                 return;
3872             }
3873             CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager());
3874 
3875             Intent clickIntent = new Intent(mContext, SyncActivityTooManyDeletes.class);
3876             clickIntent.putExtra("account", account);
3877             clickIntent.putExtra("authority", authority);
3878             clickIntent.putExtra("provider", authorityName.toString());
3879             clickIntent.putExtra("numDeletes", numDeletes);
3880 
3881             if (!isActivityAvailable(clickIntent)) {
3882                 Log.w(TAG, "No activity found to handle too many deletes.");
3883                 return;
3884             }
3885 
3886             UserHandle user = new UserHandle(userId);
3887             final PendingIntent pendingIntent = PendingIntent
3888                     .getActivityAsUser(mContext, 0, clickIntent,
3889                             PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
3890                             null, user);
3891 
3892             CharSequence tooManyDeletesDescFormat = mContext.getResources().getText(
3893                     R.string.contentServiceTooManyDeletesNotificationDesc);
3894 
3895             Context contextForUser = getContextForUser(user);
3896             Notification notification =
3897                     new Notification.Builder(contextForUser, SystemNotificationChannels.ACCOUNT)
3898                     .setSmallIcon(R.drawable.stat_notify_sync_error)
3899                     .setTicker(mContext.getString(R.string.contentServiceSync))
3900                     .setWhen(System.currentTimeMillis())
3901                     .setColor(contextForUser.getColor(
3902                             com.android.internal.R.color.system_notification_accent_color))
3903                     .setContentTitle(contextForUser.getString(
3904                             R.string.contentServiceSyncNotificationTitle))
3905                     .setContentText(
3906                             String.format(tooManyDeletesDescFormat.toString(), authorityName))
3907                     .setContentIntent(pendingIntent)
3908                     .build();
3909             notification.flags |= Notification.FLAG_ONGOING_EVENT;
3910             mNotificationMgr.notifyAsUser(
3911                     Integer.toString(account.hashCode() ^ authority.hashCode()),
3912                     SystemMessage.NOTE_SYNC_ERROR,
3913                     notification, user);
3914         }
3915 
3916         /**
3917          * Checks whether an activity exists on the system image for the given intent.
3918          *
3919          * @param intent The intent for an activity.
3920          * @return Whether or not an activity exists.
3921          */
isActivityAvailable(Intent intent)3922         private boolean isActivityAvailable(Intent intent) {
3923             PackageManager pm = mContext.getPackageManager();
3924             List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
3925             int listSize = list.size();
3926             for (int i = 0; i < listSize; i++) {
3927                 ResolveInfo resolveInfo = list.get(i);
3928                 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
3929                         != 0) {
3930                     return true;
3931                 }
3932             }
3933 
3934             return false;
3935         }
3936 
insertStartSyncEvent(SyncOperation syncOperation)3937         public long insertStartSyncEvent(SyncOperation syncOperation) {
3938             final long now = System.currentTimeMillis();
3939             EventLog.writeEvent(2720,
3940                     syncOperation.toEventLog(SyncStorageEngine.EVENT_START));
3941             return mSyncStorageEngine.insertStartSyncEvent(syncOperation, now);
3942         }
3943 
stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, int upstreamActivity, int downstreamActivity, long elapsedTime)3944         public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
3945                 int upstreamActivity, int downstreamActivity, long elapsedTime) {
3946             EventLog.writeEvent(2720,
3947                     syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP));
3948             mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
3949                     resultMessage, downstreamActivity, upstreamActivity,
3950                     syncOperation.owningPackage, syncOperation.target.userId);
3951         }
3952     }
3953 
isSyncStillActiveH(ActiveSyncContext activeSyncContext)3954     private boolean isSyncStillActiveH(ActiveSyncContext activeSyncContext) {
3955         for (ActiveSyncContext sync : mActiveSyncContexts) {
3956             if (sync == activeSyncContext) {
3957                 return true;
3958             }
3959         }
3960         return false;
3961     }
3962 
3963     /**
3964      * Sync extra comparison function.
3965      * @param b1 bundle to compare
3966      * @param b2 other bundle to compare
3967      * @param includeSyncSettings if false, ignore system settings in bundle.
3968      */
syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSyncSettings)3969     public static boolean syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSyncSettings) {
3970         if (b1 == b2) {
3971             return true;
3972         }
3973         // Exit early if we can.
3974         if (includeSyncSettings && b1.size() != b2.size()) {
3975             return false;
3976         }
3977         Bundle bigger = b1.size() > b2.size() ? b1 : b2;
3978         Bundle smaller = b1.size() > b2.size() ? b2 : b1;
3979         for (String key : bigger.keySet()) {
3980             if (!includeSyncSettings && isSyncSetting(key)) {
3981                 continue;
3982             }
3983             if (!smaller.containsKey(key)) {
3984                 return false;
3985             }
3986             if (!Objects.equals(bigger.get(key), smaller.get(key))) {
3987                 return false;
3988             }
3989         }
3990         return true;
3991     }
3992 
3993     /**
3994      * @return true if the provided key is used by the SyncManager in scheduling the sync.
3995      */
isSyncSetting(String key)3996     private static boolean isSyncSetting(String key) {
3997         if (key == null) {
3998             return false;
3999         }
4000         if (key.equals(ContentResolver.SYNC_EXTRAS_EXPEDITED)) {
4001             return true;
4002         }
4003         if (key.equals(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB)) {
4004             return true;
4005         }
4006         if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS)) {
4007             return true;
4008         }
4009         if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF)) {
4010             return true;
4011         }
4012         if (key.equals(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY)) {
4013             return true;
4014         }
4015         if (key.equals(ContentResolver.SYNC_EXTRAS_MANUAL)) {
4016             return true;
4017         }
4018         if (key.equals(ContentResolver.SYNC_EXTRAS_UPLOAD)) {
4019             return true;
4020         }
4021         if (key.equals(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS)) {
4022             return true;
4023         }
4024         if (key.equals(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS)) {
4025             return true;
4026         }
4027         if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD)) {
4028             return true;
4029         }
4030         if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD)) {
4031             return true;
4032         }
4033         if (key.equals(ContentResolver.SYNC_EXTRAS_PRIORITY)) {
4034             return true;
4035         }
4036         if (key.equals(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED)) {
4037             return true;
4038         }
4039         if (key.equals(ContentResolver.SYNC_EXTRAS_INITIALIZE)) {
4040             return true;
4041         }
4042 //        if (key.equals(ContentResolver.SYNC_EXTRAS_APP_STANDBY_EXEMPTED)) {
4043 //            return true;
4044 //        }
4045         // No need to check virtual flags such as SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC.
4046         return false;
4047     }
4048 
4049     static class PrintTable {
4050         private ArrayList<String[]> mTable = Lists.newArrayList();
4051         private final int mCols;
4052 
PrintTable(int cols)4053         PrintTable(int cols) {
4054             mCols = cols;
4055         }
4056 
set(int row, int col, Object... values)4057         void set(int row, int col, Object... values) {
4058             if (col + values.length > mCols) {
4059                 throw new IndexOutOfBoundsException("Table only has " + mCols +
4060                         " columns. can't set " + values.length + " at column " + col);
4061             }
4062             for (int i = mTable.size(); i <= row; i++) {
4063                 final String[] list = new String[mCols];
4064                 mTable.add(list);
4065                 for (int j = 0; j < mCols; j++) {
4066                     list[j] = "";
4067                 }
4068             }
4069             final String[] rowArray = mTable.get(row);
4070             for (int i = 0; i < values.length; i++) {
4071                 final Object value = values[i];
4072                 rowArray[col + i] = (value == null) ? "" : value.toString();
4073             }
4074         }
4075 
writeTo(PrintWriter out)4076         void writeTo(PrintWriter out) {
4077             final String[] formats = new String[mCols];
4078             int totalLength = 0;
4079             for (int col = 0; col < mCols; ++col) {
4080                 int maxLength = 0;
4081                 for (Object[] row : mTable) {
4082                     final int length = row[col].toString().length();
4083                     if (length > maxLength) {
4084                         maxLength = length;
4085                     }
4086                 }
4087                 totalLength += maxLength;
4088                 formats[col] = String.format("%%-%ds", maxLength);
4089             }
4090             formats[mCols - 1] = "%s";
4091             printRow(out, formats, mTable.get(0));
4092             totalLength += (mCols - 1) * 2;
4093             for (int i = 0; i < totalLength; ++i) {
4094                 out.print("-");
4095             }
4096             out.println();
4097             for (int i = 1, mTableSize = mTable.size(); i < mTableSize; i++) {
4098                 Object[] row = mTable.get(i);
4099                 printRow(out, formats, row);
4100             }
4101         }
4102 
printRow(PrintWriter out, String[] formats, Object[] row)4103         private void printRow(PrintWriter out, String[] formats, Object[] row) {
4104             for (int j = 0, rowLength = row.length; j < rowLength; j++) {
4105                 out.printf(String.format(formats[j], row[j].toString()));
4106                 out.print("  ");
4107             }
4108             out.println();
4109         }
4110 
getNumRows()4111         public int getNumRows() {
4112             return mTable.size();
4113         }
4114     }
4115 
getContextForUser(UserHandle user)4116     private Context getContextForUser(UserHandle user) {
4117         try {
4118             return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
4119         } catch (NameNotFoundException e) {
4120             // Default to mContext, not finding the package system is running as is unlikely.
4121             return mContext;
4122         }
4123     }
4124 
cancelJob(SyncOperation op, String why)4125     private void cancelJob(SyncOperation op, String why) {
4126         if (op == null) {
4127             Slog.wtf(TAG, "Null sync operation detected.");
4128             return;
4129         }
4130         if (op.isPeriodic) {
4131             mLogger.log("Removing periodic sync ", op, " for ", why);
4132         }
4133         getJobScheduler().cancel(op.jobId);
4134     }
4135 
resetTodayStats()4136     public void resetTodayStats() {
4137         mSyncStorageEngine.resetTodayStats(/*force=*/ true);
4138     }
4139 
wasPackageEverLaunched(String packageName, int userId)4140     private boolean wasPackageEverLaunched(String packageName, int userId) {
4141         try {
4142             return mPackageManagerInternal.wasPackageEverLaunched(packageName, userId);
4143         } catch (IllegalArgumentException e) {
4144             return false; // Package has been removed.
4145         }
4146     }
4147 }
4148