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