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