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