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