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