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.TimeMigrationUtils;
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         return TimeMigrationUtils.formatMillisWithFixedFormat(time);
1991     }
1992 
1993     private final static Comparator<SyncOperation> sOpDumpComparator = (op1, op2) -> {
1994         int res = Integer.compare(op1.target.userId, op2.target.userId);
1995         if (res != 0) return res;
1996 
1997         final Comparator<String> stringComparator = String.CASE_INSENSITIVE_ORDER;
1998 
1999         res = stringComparator.compare(op1.target.account.type, op2.target.account.type);
2000         if (res != 0) return res;
2001 
2002         res = stringComparator.compare(op1.target.account.name, op2.target.account.name);
2003         if (res != 0) return res;
2004 
2005         res = stringComparator.compare(op1.target.provider, op2.target.provider);
2006         if (res != 0) return res;
2007 
2008         res = Integer.compare(op1.reason, op2.reason);
2009         if (res != 0) return res;
2010 
2011         res = Long.compare(op1.periodMillis, op2.periodMillis);
2012         if (res != 0) return res;
2013 
2014         res = Long.compare(op1.expectedRuntime, op2.expectedRuntime);
2015         if (res != 0) return res;
2016 
2017         res = Long.compare(op1.jobId, op2.jobId);
2018         if (res != 0) return res;
2019 
2020         return 0;
2021     };
2022 
2023     private final static Comparator<SyncOperation> sOpRuntimeComparator = (op1, op2) -> {
2024         int res = Long.compare(op1.expectedRuntime, op2.expectedRuntime);
2025         if (res != 0) return res;
2026 
2027         return sOpDumpComparator.compare(op1, op2);
2028     };
2029 
countIf(Collection<T> col, Predicate<T> p)2030     private static <T> int countIf(Collection<T> col, Predicate<T> p) {
2031         int ret = 0;
2032         for (T item : col) {
2033             if (p.test(item)) ret++;
2034         }
2035         return ret;
2036     }
2037 
dumpPendingSyncs(PrintWriter pw, SyncAdapterStateFetcher buckets)2038     protected void dumpPendingSyncs(PrintWriter pw, SyncAdapterStateFetcher buckets) {
2039         List<SyncOperation> pendingSyncs = getAllPendingSyncs();
2040 
2041         pw.print("Pending Syncs: ");
2042         pw.println(countIf(pendingSyncs, op -> !op.isPeriodic));
2043 
2044         Collections.sort(pendingSyncs, sOpRuntimeComparator);
2045         int count = 0;
2046         for (SyncOperation op: pendingSyncs) {
2047             if (!op.isPeriodic) {
2048                 pw.println(op.dump(null, false, buckets, /*logSafe=*/ false));
2049                 count++;
2050             }
2051         }
2052         pw.println();
2053     }
2054 
dumpPeriodicSyncs(PrintWriter pw, SyncAdapterStateFetcher buckets)2055     protected void dumpPeriodicSyncs(PrintWriter pw, SyncAdapterStateFetcher buckets) {
2056         List<SyncOperation> pendingSyncs = getAllPendingSyncs();
2057 
2058         pw.print("Periodic Syncs: ");
2059         pw.println(countIf(pendingSyncs, op -> op.isPeriodic));
2060 
2061         Collections.sort(pendingSyncs, sOpDumpComparator);
2062         int count = 0;
2063         for (SyncOperation op: pendingSyncs) {
2064             if (op.isPeriodic) {
2065                 pw.println(op.dump(null, false, buckets, /*logSafe=*/ false));
2066                 count++;
2067             }
2068         }
2069         pw.println();
2070     }
2071 
2072     /**
2073      * Similar to {@link android.util.TimeUtils#formatDuration}, but it's more suitable and concise
2074      * for the sync manager dumpsys.  (Don't add the leading + sign, don't show milliseconds.)
2075      */
formatDurationHMS(StringBuilder sb, long duration)2076     public static StringBuilder formatDurationHMS(StringBuilder sb, long duration) {
2077         duration /= 1000;
2078         if (duration < 0) {
2079             sb.append('-');
2080             duration = -duration;
2081         }
2082         final long seconds = duration % 60;
2083         duration /= 60;
2084 
2085         final long minutes = duration % 60;
2086         duration /= 60;
2087 
2088         final long hours = duration % 24;
2089         duration /= 24;
2090 
2091         final long days = duration;
2092 
2093         boolean print = false;
2094         if (days > 0) {
2095             sb.append(days);
2096             sb.append('d');
2097             print = true;
2098         }
2099         print = printTwoDigitNumber(sb, hours, 'h', print);
2100         print = printTwoDigitNumber(sb, minutes, 'm', print);
2101         print = printTwoDigitNumber(sb, seconds, 's', print);
2102         if (!print) {
2103             sb.append("0s");
2104         }
2105 
2106         return sb;
2107     }
2108 
printTwoDigitNumber(StringBuilder sb, long value, char unit, boolean always)2109     private static boolean printTwoDigitNumber(StringBuilder sb, long value, char unit,
2110             boolean always) {
2111         if (!always && (value == 0)) {
2112             return false;
2113         }
2114         if (always && (value < 10)) {
2115             sb.append('0');
2116         }
2117         sb.append(value);
2118         sb.append(unit);
2119         return true;
2120     }
2121 
dumpSyncState(PrintWriter pw, SyncAdapterStateFetcher buckets)2122     protected void dumpSyncState(PrintWriter pw, SyncAdapterStateFetcher buckets) {
2123         final StringBuilder sb = new StringBuilder();
2124 
2125         pw.print("Data connected: "); pw.println(mDataConnectionIsConnected);
2126         pw.print("Battery saver: ");
2127         pw.println((mPowerManager != null) && mPowerManager.isPowerSaveMode());
2128 
2129         pw.print("Background network restriction: ");
2130         {
2131             final ConnectivityManager cm = getConnectivityManager();
2132             final int status = (cm == null) ? -1 : cm.getRestrictBackgroundStatus();
2133             switch (status) {
2134                 case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED:
2135                     pw.println(" disabled");
2136                     break;
2137                 case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED:
2138                     pw.println(" whitelisted");
2139                     break;
2140                 case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED:
2141                     pw.println(" enabled");
2142                     break;
2143                 default:
2144                     pw.print("Unknown(");
2145                     pw.print(status);
2146                     pw.println(")");
2147                     break;
2148             }
2149         }
2150 
2151         pw.print("Auto sync: ");
2152         List<UserInfo> users = getAllUsers();
2153         if (users != null) {
2154             for (UserInfo user : users) {
2155                 pw.print("u" + user.id + "="
2156                         + mSyncStorageEngine.getMasterSyncAutomatically(user.id) + " ");
2157             }
2158             pw.println();
2159         }
2160         pw.print("Memory low: "); pw.println(mStorageIsLow);
2161         pw.print("Device idle: "); pw.println(mDeviceIsIdle);
2162         pw.print("Reported active: "); pw.println(mReportedSyncActive);
2163         pw.print("Clock valid: "); pw.println(mSyncStorageEngine.isClockValid());
2164 
2165         final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts();
2166 
2167         pw.print("Accounts: ");
2168         if (accounts != INITIAL_ACCOUNTS_ARRAY) {
2169             pw.println(accounts.length);
2170         } else {
2171             pw.println("not known yet");
2172         }
2173         final long now = SystemClock.elapsedRealtime();
2174         pw.print("Now: "); pw.print(now);
2175         pw.println(" (" + formatTime(System.currentTimeMillis()) + ")");
2176 
2177         sb.setLength(0);
2178         pw.print("Uptime: "); pw.print(formatDurationHMS(sb, now));
2179         pw.println();
2180         pw.print("Time spent syncing: ");
2181 
2182         sb.setLength(0);
2183         pw.print(formatDurationHMS(sb,
2184                 mSyncHandler.mSyncTimeTracker.timeSpentSyncing()));
2185         pw.print(", sync ");
2186         pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not ");
2187         pw.println("in progress");
2188 
2189         pw.println();
2190         pw.println("Active Syncs: " + mActiveSyncContexts.size());
2191         final PackageManager pm = mContext.getPackageManager();
2192         for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
2193             final long durationInSeconds = (now - activeSyncContext.mStartTime);
2194             pw.print("  ");
2195             sb.setLength(0);
2196             pw.print(formatDurationHMS(sb, durationInSeconds));
2197             pw.print(" - ");
2198             pw.print(activeSyncContext.mSyncOperation.dump(pm, false, buckets, /*logSafe=*/ false));
2199             pw.println();
2200         }
2201         pw.println();
2202 
2203         dumpPendingSyncs(pw, buckets);
2204         dumpPeriodicSyncs(pw, buckets);
2205 
2206         // Join the installed sync adapter with the accounts list and emit for everything.
2207         pw.println("Sync Status");
2208 
2209         final ArrayList<Pair<EndPoint, SyncStatusInfo>> statuses = new ArrayList<>();
2210 
2211         mSyncStorageEngine.resetTodayStats(/* force=*/ false);
2212 
2213         for (AccountAndUser account : accounts) {
2214             final boolean unlocked;
2215             synchronized (mUnlockedUsers) {
2216                 unlocked = mUnlockedUsers.get(account.userId);
2217             }
2218             pw.printf("Account %s u%d %s%s\n",
2219                     account.account.name, account.userId, account.account.type,
2220                     (unlocked ? "" : " (locked)"));
2221 
2222             pw.println("=======================================================================");
2223             final PrintTable table = new PrintTable(16);
2224             table.set(0, 0,
2225                     "Authority", // 0
2226                     "Syncable",  // 1
2227                     "Enabled",   // 2
2228 
2229                     "Stats",     // 3 "Total", "Today" or "Yesterday".
2230 
2231                     "Loc",       // 4 # of syncs with local sources. (including failures/cancels. )
2232                     "Poll",      // 5 "poll" syncs.
2233                     "Per",       // 6 Periodic syncs.
2234                     "Feed",      // 7 Syncs with a "feed" extra. (subscribedfeeds?)
2235                     "User",      // 8 User-initiated
2236                     "Othr",      // 9 Other sources.
2237 
2238                     "Tot",       // 10 Total syncs (including failures / cancels)
2239                     "Fail",      // 11 (Failure)
2240                     "Can",       // 12 (Cancel)
2241 
2242                     "Time",      // 13 Total time
2243                     "Last Sync", // 14
2244                     "Backoff"    // 15
2245             );
2246 
2247             final List<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> sorted =
2248                     Lists.newArrayList();
2249             sorted.addAll(mSyncAdapters.getAllServices(account.userId));
2250             Collections.sort(sorted,
2251                     new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() {
2252                         @Override
2253                         public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs,
2254                                 RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
2255                             return lhs.type.authority.compareTo(rhs.type.authority);
2256                         }
2257                     });
2258             for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : sorted) {
2259                 if (!syncAdapterType.type.accountType.equals(account.account.type)) {
2260                     continue;
2261                 }
2262                 int row = table.getNumRows();
2263                 Pair<AuthorityInfo, SyncStatusInfo> syncAuthoritySyncStatus =
2264                         mSyncStorageEngine.getCopyOfAuthorityWithSyncStatus(
2265                                 new SyncStorageEngine.EndPoint(
2266                                         account.account,
2267                                         syncAdapterType.type.authority,
2268                                         account.userId));
2269                 SyncStorageEngine.AuthorityInfo settings = syncAuthoritySyncStatus.first;
2270                 SyncStatusInfo status = syncAuthoritySyncStatus.second;
2271                 statuses.add(Pair.create(settings.target, status));
2272                 String authority = settings.target.provider;
2273                 if (authority.length() > 50) {
2274                     authority = authority.substring(authority.length() - 50);
2275                 }
2276                 table.set(row, 0, authority, settings.syncable, settings.enabled);
2277 
2278                 QuadConsumer<String, Stats, Function<Integer, String>, Integer> c =
2279                         (label, stats, filter, r) -> {
2280                     sb.setLength(0);
2281                     table.set(r, 3,
2282                             label,
2283                             filter.apply(stats.numSourceLocal),
2284                             filter.apply(stats.numSourcePoll),
2285                             filter.apply(stats.numSourcePeriodic),
2286                             filter.apply(stats.numSourceFeed),
2287                             filter.apply(stats.numSourceUser),
2288                             filter.apply(stats.numSourceOther),
2289                             filter.apply(stats.numSyncs),
2290                             filter.apply(stats.numFailures),
2291                             filter.apply(stats.numCancels),
2292                             formatDurationHMS(sb, stats.totalElapsedTime));
2293                 };
2294                 c.accept("Total", status.totalStats, (i) -> Integer.toString(i), row);
2295                 c.accept("Today", status.todayStats, this::zeroToEmpty, row + 1);
2296                 c.accept("Yestr", status.yesterdayStats, this::zeroToEmpty, row + 2);
2297 
2298                 final int LAST_SYNC = 14;
2299                 final int BACKOFF = LAST_SYNC + 1;
2300 
2301                 int row1 = row;
2302                 if (settings.delayUntil > now) {
2303                     table.set(row1++, BACKOFF, "D: " + (settings.delayUntil - now) / 1000);
2304                     if (settings.backoffTime > now) {
2305                         table.set(row1++, BACKOFF, "B: " + (settings.backoffTime - now) / 1000);
2306                         table.set(row1++, BACKOFF, settings.backoffDelay / 1000);
2307                     }
2308                 }
2309 
2310                 row1 = row;
2311                 if (status.lastSuccessTime != 0) {
2312                     table.set(row1++, LAST_SYNC, SyncStorageEngine.SOURCES[status.lastSuccessSource]
2313                             + " " + "SUCCESS");
2314                     table.set(row1++, LAST_SYNC, formatTime(status.lastSuccessTime));
2315                 }
2316                 if (status.lastFailureTime != 0) {
2317                     table.set(row1++, LAST_SYNC, SyncStorageEngine.SOURCES[status.lastFailureSource]
2318                             + " " + "FAILURE");
2319                     table.set(row1++, LAST_SYNC, formatTime(status.lastFailureTime));
2320                     //noinspection UnusedAssignment
2321                     table.set(row1++, LAST_SYNC, status.lastFailureMesg);
2322                 }
2323             }
2324             table.writeTo(pw);
2325         }
2326 
2327         dumpSyncHistory(pw);
2328 
2329         pw.println();
2330         pw.println("Per Adapter History");
2331         pw.println("(SERVER is now split up to FEED and OTHER)");
2332 
2333         for (int i = 0; i < statuses.size(); i++) {
2334             final Pair<EndPoint, SyncStatusInfo> event = statuses.get(i);
2335 
2336             pw.print("  ");
2337             pw.print(event.first.account.name);
2338             pw.print('/');
2339             pw.print(event.first.account.type);
2340             pw.print(" u");
2341             pw.print(event.first.userId);
2342             pw.print(" [");
2343             pw.print(event.first.provider);
2344             pw.print("]");
2345             pw.println();
2346 
2347             pw.println("    Per source last syncs:");
2348             for (int j = 0; j < SyncStorageEngine.SOURCES.length; j++) {
2349                 pw.print("      ");
2350                 pw.print(String.format("%8s", SyncStorageEngine.SOURCES[j]));
2351                 pw.print("  Success: ");
2352                 pw.print(formatTime(event.second.perSourceLastSuccessTimes[j]));
2353 
2354                 pw.print("  Failure: ");
2355                 pw.println(formatTime(event.second.perSourceLastFailureTimes[j]));
2356             }
2357 
2358             pw.println("    Last syncs:");
2359             for (int j = 0; j < event.second.getEventCount(); j++) {
2360                 pw.print("      ");
2361                 pw.print(formatTime(event.second.getEventTime(j)));
2362                 pw.print(' ');
2363                 pw.print(event.second.getEvent(j));
2364                 pw.println();
2365             }
2366             if (event.second.getEventCount() == 0) {
2367                 pw.println("      N/A");
2368             }
2369         }
2370     }
2371 
zeroToEmpty(int value)2372     private String zeroToEmpty(int value) {
2373         return (value != 0) ? Integer.toString(value) : "";
2374     }
2375 
dumpTimeSec(PrintWriter pw, long time)2376     private void dumpTimeSec(PrintWriter pw, long time) {
2377         pw.print(time/1000); pw.print('.'); pw.print((time/100)%10);
2378         pw.print('s');
2379     }
2380 
dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds)2381     private void dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds) {
2382         pw.print("Success ("); pw.print(ds.successCount);
2383         if (ds.successCount > 0) {
2384             pw.print(" for "); dumpTimeSec(pw, ds.successTime);
2385             pw.print(" avg="); dumpTimeSec(pw, ds.successTime/ds.successCount);
2386         }
2387         pw.print(") Failure ("); pw.print(ds.failureCount);
2388         if (ds.failureCount > 0) {
2389             pw.print(" for "); dumpTimeSec(pw, ds.failureTime);
2390             pw.print(" avg="); dumpTimeSec(pw, ds.failureTime/ds.failureCount);
2391         }
2392         pw.println(")");
2393     }
2394 
dumpSyncHistory(PrintWriter pw)2395     protected void dumpSyncHistory(PrintWriter pw) {
2396         dumpRecentHistory(pw);
2397         dumpDayStatistics(pw);
2398     }
2399 
dumpRecentHistory(PrintWriter pw)2400     private void dumpRecentHistory(PrintWriter pw) {
2401         final ArrayList<SyncStorageEngine.SyncHistoryItem> items
2402                 = mSyncStorageEngine.getSyncHistory();
2403         if (items != null && items.size() > 0) {
2404             final Map<String, AuthoritySyncStats> authorityMap = Maps.newHashMap();
2405             long totalElapsedTime = 0;
2406             long totalTimes = 0;
2407             final int N = items.size();
2408 
2409             int maxAuthority = 0;
2410             int maxAccount = 0;
2411             for (SyncStorageEngine.SyncHistoryItem item : items) {
2412                 SyncStorageEngine.AuthorityInfo authorityInfo
2413                         = mSyncStorageEngine.getAuthority(item.authorityId);
2414                 final String authorityName;
2415                 final String accountKey;
2416                 if (authorityInfo != null) {
2417                     authorityName = authorityInfo.target.provider;
2418                     accountKey = authorityInfo.target.account.name + "/"
2419                             + authorityInfo.target.account.type
2420                             + " u" + authorityInfo.target.userId;
2421                 } else {
2422                     authorityName = "Unknown";
2423                     accountKey = "Unknown";
2424                 }
2425 
2426                 int length = authorityName.length();
2427                 if (length > maxAuthority) {
2428                     maxAuthority = length;
2429                 }
2430                 length = accountKey.length();
2431                 if (length > maxAccount) {
2432                     maxAccount = length;
2433                 }
2434 
2435                 final long elapsedTime = item.elapsedTime;
2436                 totalElapsedTime += elapsedTime;
2437                 totalTimes++;
2438                 AuthoritySyncStats authoritySyncStats = authorityMap.get(authorityName);
2439                 if (authoritySyncStats == null) {
2440                     authoritySyncStats = new AuthoritySyncStats(authorityName);
2441                     authorityMap.put(authorityName, authoritySyncStats);
2442                 }
2443                 authoritySyncStats.elapsedTime += elapsedTime;
2444                 authoritySyncStats.times++;
2445                 final Map<String, AccountSyncStats> accountMap = authoritySyncStats.accountMap;
2446                 AccountSyncStats accountSyncStats = accountMap.get(accountKey);
2447                 if (accountSyncStats == null) {
2448                     accountSyncStats = new AccountSyncStats(accountKey);
2449                     accountMap.put(accountKey, accountSyncStats);
2450                 }
2451                 accountSyncStats.elapsedTime += elapsedTime;
2452                 accountSyncStats.times++;
2453 
2454             }
2455 
2456             if (totalElapsedTime > 0) {
2457                 pw.println();
2458                 pw.printf("Detailed Statistics (Recent history):  "
2459                                 + "%d (# of times) %ds (sync time)\n",
2460                         totalTimes, totalElapsedTime / 1000);
2461 
2462                 final List<AuthoritySyncStats> sortedAuthorities =
2463                         new ArrayList<AuthoritySyncStats>(authorityMap.values());
2464                 Collections.sort(sortedAuthorities, new Comparator<AuthoritySyncStats>() {
2465                     @Override
2466                     public int compare(AuthoritySyncStats lhs, AuthoritySyncStats rhs) {
2467                         // reverse order
2468                         int compare = Integer.compare(rhs.times, lhs.times);
2469                         if (compare == 0) {
2470                             compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime);
2471                         }
2472                         return compare;
2473                     }
2474                 });
2475 
2476                 final int maxLength = Math.max(maxAuthority, maxAccount + 3);
2477                 final int padLength = 2 + 2 + maxLength + 2 + 10 + 11;
2478                 final char chars[] = new char[padLength];
2479                 Arrays.fill(chars, '-');
2480                 final String separator = new String(chars);
2481 
2482                 final String authorityFormat =
2483                         String.format("  %%-%ds: %%-9s  %%-11s\n", maxLength + 2);
2484                 final String accountFormat =
2485                         String.format("    %%-%ds:   %%-9s  %%-11s\n", maxLength);
2486 
2487                 pw.println(separator);
2488                 for (AuthoritySyncStats authoritySyncStats : sortedAuthorities) {
2489                     String name = authoritySyncStats.name;
2490                     long elapsedTime;
2491                     int times;
2492                     String timeStr;
2493                     String timesStr;
2494 
2495                     elapsedTime = authoritySyncStats.elapsedTime;
2496                     times = authoritySyncStats.times;
2497                     timeStr = String.format("%ds/%d%%",
2498                             elapsedTime / 1000,
2499                             elapsedTime * 100 / totalElapsedTime);
2500                     timesStr = String.format("%d/%d%%",
2501                             times,
2502                             times * 100 / totalTimes);
2503                     pw.printf(authorityFormat, name, timesStr, timeStr);
2504 
2505                     final List<AccountSyncStats> sortedAccounts =
2506                             new ArrayList<AccountSyncStats>(
2507                                     authoritySyncStats.accountMap.values());
2508                     Collections.sort(sortedAccounts, new Comparator<AccountSyncStats>() {
2509                         @Override
2510                         public int compare(AccountSyncStats lhs, AccountSyncStats rhs) {
2511                             // reverse order
2512                             int compare = Integer.compare(rhs.times, lhs.times);
2513                             if (compare == 0) {
2514                                 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime);
2515                             }
2516                             return compare;
2517                         }
2518                     });
2519                     for (AccountSyncStats stats: sortedAccounts) {
2520                         elapsedTime = stats.elapsedTime;
2521                         times = stats.times;
2522                         timeStr = String.format("%ds/%d%%",
2523                                 elapsedTime / 1000,
2524                                 elapsedTime * 100 / totalElapsedTime);
2525                         timesStr = String.format("%d/%d%%",
2526                                 times,
2527                                 times * 100 / totalTimes);
2528                         pw.printf(accountFormat, stats.name, timesStr, timeStr);
2529                     }
2530                     pw.println(separator);
2531                 }
2532             }
2533 
2534             pw.println();
2535             pw.println("Recent Sync History");
2536             pw.println("(SERVER is now split up to FEED and OTHER)");
2537             final String format = "  %-" + maxAccount + "s  %-" + maxAuthority + "s %s\n";
2538             final Map<String, Long> lastTimeMap = Maps.newHashMap();
2539             final PackageManager pm = mContext.getPackageManager();
2540             for (int i = 0; i < N; i++) {
2541                 SyncStorageEngine.SyncHistoryItem item = items.get(i);
2542                 SyncStorageEngine.AuthorityInfo authorityInfo
2543                         = mSyncStorageEngine.getAuthority(item.authorityId);
2544                 final String authorityName;
2545                 final String accountKey;
2546                 if (authorityInfo != null) {
2547                     authorityName = authorityInfo.target.provider;
2548                     accountKey = authorityInfo.target.account.name + "/"
2549                             + authorityInfo.target.account.type
2550                             + " u" + authorityInfo.target.userId;
2551                 } else {
2552                     authorityName = "Unknown";
2553                     accountKey = "Unknown";
2554                 }
2555                 final long elapsedTime = item.elapsedTime;
2556                 final long eventTime = item.eventTime;
2557 
2558                 final String key = authorityName + "/" + accountKey;
2559                 final Long lastEventTime = lastTimeMap.get(key);
2560                 final String diffString;
2561                 if (lastEventTime == null) {
2562                     diffString = "";
2563                 } else {
2564                     final long diff = (lastEventTime - eventTime) / 1000;
2565                     if (diff < 60) {
2566                         diffString = String.valueOf(diff);
2567                     } else if (diff < 3600) {
2568                         diffString = String.format("%02d:%02d", diff / 60, diff % 60);
2569                     } else {
2570                         final long sec = diff % 3600;
2571                         diffString = String.format("%02d:%02d:%02d",
2572                                 diff / 3600, sec / 60, sec % 60);
2573                     }
2574                 }
2575                 lastTimeMap.put(key, eventTime);
2576 
2577                 pw.printf("  #%-3d: %s %8s  %5.1fs  %8s",
2578                         i + 1,
2579                         formatTime(eventTime),
2580                         SyncStorageEngine.SOURCES[item.source],
2581                         ((float) elapsedTime) / 1000,
2582                         diffString);
2583                 pw.printf(format, accountKey, authorityName,
2584                         SyncOperation.reasonToString(pm, item.reason));
2585 
2586                 if (item.event != SyncStorageEngine.EVENT_STOP
2587                         || item.upstreamActivity != 0
2588                         || item.downstreamActivity != 0) {
2589                     pw.printf("    event=%d upstreamActivity=%d downstreamActivity=%d\n",
2590                             item.event,
2591                             item.upstreamActivity,
2592                             item.downstreamActivity);
2593                 }
2594                 if (item.mesg != null
2595                         && !SyncStorageEngine.MESG_SUCCESS.equals(item.mesg)) {
2596                     pw.printf("    mesg=%s\n", item.mesg);
2597                 }
2598             }
2599             pw.println();
2600             pw.println("Recent Sync History Extras");
2601             pw.println("(SERVER is now split up to FEED and OTHER)");
2602             for (int i = 0; i < N; i++) {
2603                 final SyncStorageEngine.SyncHistoryItem item = items.get(i);
2604                 final Bundle extras = item.extras;
2605                 if (extras == null || extras.size() == 0) {
2606                     continue;
2607                 }
2608                 final SyncStorageEngine.AuthorityInfo authorityInfo
2609                         = mSyncStorageEngine.getAuthority(item.authorityId);
2610                 final String authorityName;
2611                 final String accountKey;
2612                 if (authorityInfo != null) {
2613                     authorityName = authorityInfo.target.provider;
2614                     accountKey = authorityInfo.target.account.name + "/"
2615                             + authorityInfo.target.account.type
2616                             + " u" + authorityInfo.target.userId;
2617                 } else {
2618                     authorityName = "Unknown";
2619                     accountKey = "Unknown";
2620                 }
2621                 final long eventTime = item.eventTime;
2622 
2623                 pw.printf("  #%-3d: %s %8s ",
2624                         i + 1,
2625                         formatTime(eventTime),
2626                         SyncStorageEngine.SOURCES[item.source]);
2627 
2628                 pw.printf(format, accountKey, authorityName, extras);
2629             }
2630         }
2631     }
2632 
dumpDayStatistics(PrintWriter pw)2633     private void dumpDayStatistics(PrintWriter pw) {
2634         SyncStorageEngine.DayStats dses[] = mSyncStorageEngine.getDayStatistics();
2635         if (dses != null && dses[0] != null) {
2636             pw.println();
2637             pw.println("Sync Statistics");
2638             pw.print("  Today:  "); dumpDayStatistic(pw, dses[0]);
2639             int today = dses[0].day;
2640             int i;
2641             SyncStorageEngine.DayStats ds;
2642 
2643             // Print each day in the current week.
2644             for (i=1; i<=6 && i < dses.length; i++) {
2645                 ds = dses[i];
2646                 if (ds == null) break;
2647                 int delta = today-ds.day;
2648                 if (delta > 6) break;
2649 
2650                 pw.print("  Day-"); pw.print(delta); pw.print(":  ");
2651                 dumpDayStatistic(pw, ds);
2652             }
2653 
2654             // Aggregate all following days into weeks and print totals.
2655             int weekDay = today;
2656             while (i < dses.length) {
2657                 SyncStorageEngine.DayStats aggr = null;
2658                 weekDay -= 7;
2659                 while (i < dses.length) {
2660                     ds = dses[i];
2661                     if (ds == null) {
2662                         i = dses.length;
2663                         break;
2664                     }
2665                     int delta = weekDay-ds.day;
2666                     if (delta > 6) break;
2667                     i++;
2668 
2669                     if (aggr == null) {
2670                         aggr = new SyncStorageEngine.DayStats(weekDay);
2671                     }
2672                     aggr.successCount += ds.successCount;
2673                     aggr.successTime += ds.successTime;
2674                     aggr.failureCount += ds.failureCount;
2675                     aggr.failureTime += ds.failureTime;
2676                 }
2677                 if (aggr != null) {
2678                     pw.print("  Week-"); pw.print((today-weekDay)/7); pw.print(": ");
2679                     dumpDayStatistic(pw, aggr);
2680                 }
2681             }
2682         }
2683     }
2684 
dumpSyncAdapters(IndentingPrintWriter pw)2685     private void dumpSyncAdapters(IndentingPrintWriter pw) {
2686         pw.println();
2687         final List<UserInfo> users = getAllUsers();
2688         if (users != null) {
2689             for (UserInfo user : users) {
2690                 pw.println("Sync adapters for " + user + ":");
2691                 pw.increaseIndent();
2692                 for (RegisteredServicesCache.ServiceInfo<?> info :
2693                         mSyncAdapters.getAllServices(user.id)) {
2694                     pw.println(info);
2695                 }
2696                 pw.decreaseIndent();
2697                 pw.println();
2698             }
2699         }
2700     }
2701 
2702     private static class AuthoritySyncStats {
2703         String name;
2704         long elapsedTime;
2705         int times;
2706         Map<String, AccountSyncStats> accountMap = Maps.newHashMap();
2707 
AuthoritySyncStats(String name)2708         private AuthoritySyncStats(String name) {
2709             this.name = name;
2710         }
2711     }
2712 
2713     private static class AccountSyncStats {
2714         String name;
2715         long elapsedTime;
2716         int times;
2717 
AccountSyncStats(String name)2718         private AccountSyncStats(String name) {
2719             this.name = name;
2720         }
2721     }
2722 
2723     interface OnReadyCallback {
onReady()2724         void onReady();
2725     }
2726 
sendOnUnsyncableAccount(@onNull Context context, @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo, @UserIdInt int userId, @NonNull OnReadyCallback onReadyCallback)2727     static void sendOnUnsyncableAccount(@NonNull Context context,
2728             @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo,
2729             @UserIdInt int userId, @NonNull OnReadyCallback onReadyCallback) {
2730         OnUnsyncableAccountCheck connection = new OnUnsyncableAccountCheck(syncAdapterInfo,
2731                 onReadyCallback);
2732 
2733         boolean isBound = context.bindServiceAsUser(
2734                 getAdapterBindIntent(context, syncAdapterInfo.componentName, userId),
2735                 connection, SYNC_ADAPTER_CONNECTION_FLAGS, UserHandle.of(userId));
2736 
2737         if (isBound) {
2738             // Unbind after SERVICE_BOUND_TIME_MILLIS to not leak the connection.
2739             (new Handler(Looper.getMainLooper())).postDelayed(
2740                     () -> context.unbindService(connection),
2741                     OnUnsyncableAccountCheck.SERVICE_BOUND_TIME_MILLIS);
2742         } else {
2743                 /*
2744                  * The default implementation of adapter.onUnsyncableAccount returns true. Hence if
2745                  * there the service cannot be bound, assume the default behavior.
2746                  */
2747             connection.onReady();
2748         }
2749     }
2750 
2751 
2752     /**
2753      * Helper class for calling ISyncAdapter.onUnsyncableAccountDone.
2754      *
2755      * If this returns {@code true} the onReadyCallback is called. Otherwise nothing happens.
2756      */
2757     private static class OnUnsyncableAccountCheck implements ServiceConnection {
2758         static final long SERVICE_BOUND_TIME_MILLIS = 5000;
2759 
2760         private final @NonNull OnReadyCallback mOnReadyCallback;
2761         private final @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType>
2762                 mSyncAdapterInfo;
2763 
OnUnsyncableAccountCheck( @onNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo, @NonNull OnReadyCallback onReadyCallback)2764         OnUnsyncableAccountCheck(
2765                 @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo,
2766                 @NonNull OnReadyCallback onReadyCallback) {
2767             mSyncAdapterInfo = syncAdapterInfo;
2768             mOnReadyCallback = onReadyCallback;
2769         }
2770 
onReady()2771         private void onReady() {
2772             long identity = Binder.clearCallingIdentity();
2773             try {
2774                 mOnReadyCallback.onReady();
2775             } finally {
2776                 Binder.restoreCallingIdentity(identity);
2777             }
2778         }
2779 
2780         @Override
onServiceConnected(ComponentName name, IBinder service)2781         public void onServiceConnected(ComponentName name, IBinder service) {
2782             final ISyncAdapter adapter = ISyncAdapter.Stub.asInterface(service);
2783 
2784             try {
2785                 adapter.onUnsyncableAccount(new ISyncAdapterUnsyncableAccountCallback.Stub() {
2786                     @Override
2787                     public void onUnsyncableAccountDone(boolean isReady) {
2788                         if (isReady) {
2789                             onReady();
2790                         }
2791                     }
2792                 });
2793             } catch (RemoteException e) {
2794                 Slog.e(TAG, "Could not call onUnsyncableAccountDone " + mSyncAdapterInfo, e);
2795                 /*
2796                  * The default implementation of adapter.onUnsyncableAccount returns true. Hence if
2797                  * there is a crash in the implementation, assume the default behavior.
2798                  */
2799                 onReady();
2800             }
2801         }
2802 
2803         @Override
onServiceDisconnected(ComponentName name)2804         public void onServiceDisconnected(ComponentName name) {
2805             // Wait until the service connects again
2806         }
2807     }
2808 
2809     /**
2810      * A helper object to keep track of the time we have spent syncing since the last boot
2811      */
2812     private class SyncTimeTracker {
2813         /** True if a sync was in progress on the most recent call to update() */
2814         boolean mLastWasSyncing = false;
2815         /** Used to track when lastWasSyncing was last set */
2816         long mWhenSyncStarted = 0;
2817         /** The cumulative time we have spent syncing */
2818         private long mTimeSpentSyncing;
2819 
2820         /** Call to let the tracker know that the sync state may have changed */
update()2821         public synchronized void update() {
2822             final boolean isSyncInProgress = !mActiveSyncContexts.isEmpty();
2823             if (isSyncInProgress == mLastWasSyncing) return;
2824             final long now = SystemClock.elapsedRealtime();
2825             if (isSyncInProgress) {
2826                 mWhenSyncStarted = now;
2827             } else {
2828                 mTimeSpentSyncing += now - mWhenSyncStarted;
2829             }
2830             mLastWasSyncing = isSyncInProgress;
2831         }
2832 
2833         /** Get how long we have been syncing, in ms */
timeSpentSyncing()2834         public synchronized long timeSpentSyncing() {
2835             if (!mLastWasSyncing) return mTimeSpentSyncing;
2836 
2837             final long now = SystemClock.elapsedRealtime();
2838             return mTimeSpentSyncing + (now - mWhenSyncStarted);
2839         }
2840     }
2841 
2842     class ServiceConnectionData {
2843         public final ActiveSyncContext activeSyncContext;
2844         public final IBinder adapter;
2845 
ServiceConnectionData(ActiveSyncContext activeSyncContext, IBinder adapter)2846         ServiceConnectionData(ActiveSyncContext activeSyncContext, IBinder adapter) {
2847             this.activeSyncContext = activeSyncContext;
2848             this.adapter = adapter;
2849         }
2850     }
2851 
2852     @Nullable
getInstance()2853     private static SyncManager getInstance() {
2854         synchronized (SyncManager.class) {
2855             if (sInstance == null) {
2856                 Slog.wtf(TAG, "sInstance == null"); // Maybe called too early?
2857             }
2858             return sInstance;
2859         }
2860     }
2861 
2862     /**
2863      * @return whether the device is ready to run sync jobs for a given user.
2864      */
readyToSync(int userId)2865     public static boolean readyToSync(int userId) {
2866         final SyncManager instance = getInstance();
2867         return (instance != null) && SyncJobService.isReady()
2868                 && instance.mProvisioned && instance.isUserUnlocked(userId);
2869     }
2870 
sendMessage(Message message)2871     public static void sendMessage(Message message) {
2872         final SyncManager instance = getInstance();
2873         if (instance != null) {
2874             instance.mSyncHandler.sendMessage(message);
2875         }
2876     }
2877 
2878     /**
2879      * Handles SyncOperation Messages that are posted to the associated
2880      * HandlerThread.
2881      */
2882     class SyncHandler extends Handler {
2883         // Messages that can be sent on mHandler.
2884         private static final int MESSAGE_SYNC_FINISHED = 1;
2885         private static final int MESSAGE_SERVICE_CONNECTED = 4;
2886         private static final int MESSAGE_SERVICE_DISCONNECTED = 5;
2887         private static final int MESSAGE_CANCEL = 6;
2888         static final int MESSAGE_START_SYNC = 10;
2889         static final int MESSAGE_STOP_SYNC = 11;
2890         static final int MESSAGE_SCHEDULE_SYNC = 12;
2891         static final int MESSAGE_UPDATE_PERIODIC_SYNC = 13;
2892         static final int MESSAGE_REMOVE_PERIODIC_SYNC = 14;
2893 
2894         /**
2895          * Posted periodically to monitor network process for long-running syncs.
2896          * obj: {@link com.android.server.content.SyncManager.ActiveSyncContext}
2897          */
2898         private static final int MESSAGE_MONITOR_SYNC = 8;
2899         private static final int MESSAGE_ACCOUNTS_UPDATED = 9;
2900 
2901         public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker();
2902         private final HashMap<String, PowerManager.WakeLock> mWakeLocks = Maps.newHashMap();
2903 
SyncHandler(Looper looper)2904         public SyncHandler(Looper looper) {
2905             super(looper);
2906         }
2907 
handleMessage(Message msg)2908         public void handleMessage(Message msg) {
2909             // TODO Do we really need this wake lock?? If we actually needed it, this is probably
2910             // not the best place to acquire the lock -- it's probably too late, because the device
2911             // could have gone to sleep before we reach here.
2912             mSyncManagerWakeLock.acquire();
2913             try {
2914                 handleSyncMessage(msg);
2915             } finally {
2916                 mSyncManagerWakeLock.release();
2917             }
2918         }
2919 
handleSyncMessage(Message msg)2920         private void handleSyncMessage(Message msg) {
2921             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
2922 
2923             try {
2924                 mDataConnectionIsConnected = readDataConnectionState();
2925                 switch (msg.what) {
2926                     case MESSAGE_ACCOUNTS_UPDATED:
2927                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2928                             Slog.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED");
2929                         }
2930                         EndPoint targets = (EndPoint) msg.obj;
2931                         updateRunningAccountsH(targets);
2932                         break;
2933                     case MESSAGE_SCHEDULE_SYNC:
2934                         ScheduleSyncMessagePayload syncPayload =
2935                                 (ScheduleSyncMessagePayload) msg.obj;
2936                         SyncOperation op = syncPayload.syncOperation;
2937                         scheduleSyncOperationH(op, syncPayload.minDelayMillis);
2938                         break;
2939 
2940                     case MESSAGE_START_SYNC:
2941                         op = (SyncOperation) msg.obj;
2942                         startSyncH(op);
2943                         break;
2944 
2945                     case MESSAGE_STOP_SYNC:
2946                         op = (SyncOperation) msg.obj;
2947                         if (isLoggable) {
2948                             Slog.v(TAG, "Stop sync received.");
2949                         }
2950                         ActiveSyncContext asc = findActiveSyncContextH(op.jobId);
2951                         if (asc != null) {
2952                             runSyncFinishedOrCanceledH(null /* no result */, asc);
2953                             boolean reschedule = msg.arg1 != 0;
2954                             boolean applyBackoff = msg.arg2 != 0;
2955                             if (isLoggable) {
2956                                 Slog.v(TAG, "Stopping sync. Reschedule: " + reschedule
2957                                         + "Backoff: " + applyBackoff);
2958                             }
2959                             if (applyBackoff) {
2960                                 increaseBackoffSetting(op.target);
2961                             }
2962                             if (reschedule) {
2963                                 deferStoppedSyncH(op, 0);
2964                             }
2965                         }
2966                         break;
2967 
2968                     case MESSAGE_UPDATE_PERIODIC_SYNC:
2969                         UpdatePeriodicSyncMessagePayload data =
2970                                 (UpdatePeriodicSyncMessagePayload) msg.obj;
2971                         updateOrAddPeriodicSyncH(data.target, data.pollFrequency,
2972                                 data.flex, data.extras);
2973                         break;
2974                     case MESSAGE_REMOVE_PERIODIC_SYNC:
2975                         Pair<EndPoint, String> args = (Pair<EndPoint, String>) (msg.obj);
2976                         removePeriodicSyncH(args.first, msg.getData(), args.second);
2977                         break;
2978 
2979                     case SyncHandler.MESSAGE_CANCEL:
2980                         SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj;
2981                         Bundle extras = msg.peekData();
2982                         if (isLoggable) {
2983                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_CANCEL: "
2984                                     + endpoint + " bundle: " + extras);
2985                         }
2986                         cancelActiveSyncH(endpoint, extras, "MESSAGE_CANCEL");
2987                         break;
2988 
2989                     case SyncHandler.MESSAGE_SYNC_FINISHED:
2990                         SyncFinishedOrCancelledMessagePayload payload =
2991                                 (SyncFinishedOrCancelledMessagePayload) msg.obj;
2992                         if (!isSyncStillActiveH(payload.activeSyncContext)) {
2993                             if (isLoggable) {
2994                                 Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
2995                                         + "sync is no longer active: "
2996                                         + payload.activeSyncContext);
2997                             }
2998                             break;
2999                         }
3000                         if (isLoggable) {
3001                             Slog.v(TAG, "syncFinished" + payload.activeSyncContext.mSyncOperation);
3002                         }
3003                         SyncJobService.callJobFinished(
3004                                 payload.activeSyncContext.mSyncOperation.jobId, false,
3005                                 "sync finished");
3006                         runSyncFinishedOrCanceledH(payload.syncResult,
3007                                 payload.activeSyncContext);
3008                         break;
3009 
3010                     case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
3011                         ServiceConnectionData msgData = (ServiceConnectionData) msg.obj;
3012                         if (isLoggable) {
3013                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
3014                                     + msgData.activeSyncContext);
3015                         }
3016                         // Check that this isn't an old message.
3017                         if (isSyncStillActiveH(msgData.activeSyncContext)) {
3018                             runBoundToAdapterH(
3019                                     msgData.activeSyncContext,
3020                                     msgData.adapter);
3021                         }
3022                         break;
3023                     }
3024 
3025                     case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
3026                         final ActiveSyncContext currentSyncContext =
3027                                 ((ServiceConnectionData) msg.obj).activeSyncContext;
3028                         if (isLoggable) {
3029                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
3030                                     + currentSyncContext);
3031                         }
3032                         // Check that this isn't an old message.
3033                         if (isSyncStillActiveH(currentSyncContext)) {
3034                             // cancel the sync if we have a syncadapter, which means one is
3035                             // outstanding
3036                             try {
3037                                 if (currentSyncContext.mSyncAdapter != null) {
3038                                     mLogger.log("Calling cancelSync for SERVICE_DISCONNECTED ",
3039                                             currentSyncContext,
3040                                             " adapter=", currentSyncContext.mSyncAdapter);
3041                                     currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext);
3042                                     mLogger.log("Canceled");
3043                                 }
3044                             } catch (RemoteException e) {
3045                                 mLogger.log("RemoteException ", Log.getStackTraceString(e));
3046                                 // We don't need to retry this in this case.
3047                             }
3048 
3049                             // Pretend that the sync failed with an IOException,
3050                             // which is a soft error.
3051                             SyncResult syncResult = new SyncResult();
3052                             syncResult.stats.numIoExceptions++;
3053                             SyncJobService.callJobFinished(
3054                                     currentSyncContext.mSyncOperation.jobId, false,
3055                                     "service disconnected");
3056                             runSyncFinishedOrCanceledH(syncResult, currentSyncContext);
3057                         }
3058                         break;
3059                     }
3060 
3061                     case SyncHandler.MESSAGE_MONITOR_SYNC:
3062                         ActiveSyncContext monitoredSyncContext = (ActiveSyncContext) msg.obj;
3063                         if (isLoggable) {
3064                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_MONITOR_SYNC: " +
3065                                     monitoredSyncContext.mSyncOperation.target);
3066                         }
3067 
3068                         if (isSyncNotUsingNetworkH(monitoredSyncContext)) {
3069                             Log.w(TAG, String.format(
3070                                     "Detected sync making no progress for %s. cancelling.",
3071                                     logSafe(monitoredSyncContext)));
3072                             SyncJobService.callJobFinished(
3073                                     monitoredSyncContext.mSyncOperation.jobId, false,
3074                                     "no network activity");
3075                             runSyncFinishedOrCanceledH(
3076                                     null /* cancel => no result */, monitoredSyncContext);
3077                         } else {
3078                             // Repost message to check again.
3079                             postMonitorSyncProgressMessage(monitoredSyncContext);
3080                         }
3081                         break;
3082 
3083                 }
3084             } finally {
3085                 mSyncTimeTracker.update();
3086             }
3087         }
3088 
getSyncWakeLock(SyncOperation operation)3089         private PowerManager.WakeLock getSyncWakeLock(SyncOperation operation) {
3090             final String wakeLockKey = operation.wakeLockName();
3091             PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey);
3092             if (wakeLock == null) {
3093                 final String name = SYNC_WAKE_LOCK_PREFIX + wakeLockKey;
3094                 wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
3095                 wakeLock.setReferenceCounted(false);
3096                 mWakeLocks.put(wakeLockKey, wakeLock);
3097             }
3098             return wakeLock;
3099         }
3100 
3101         /**
3102          * Defer the specified SyncOperation by rescheduling it on the JobScheduler with some
3103          * delay. This is equivalent to a failure. If this is a periodic sync, a delayed one-off
3104          * sync will be scheduled.
3105          */
deferSyncH(SyncOperation op, long delay, String why)3106         private void deferSyncH(SyncOperation op, long delay, String why) {
3107             mLogger.log("deferSyncH() ", (op.isPeriodic ? "periodic " : ""),
3108                     "sync.  op=", op, " delay=", delay, " why=", why);
3109             SyncJobService.callJobFinished(op.jobId, false, why);
3110             if (op.isPeriodic) {
3111                 scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay);
3112             } else {
3113                 // mSyncJobService.callJobFinished is async, so cancel the job to ensure we don't
3114                 // find the this job in the pending jobs list while looking for duplicates
3115                 // before scheduling it at a later time.
3116                 cancelJob(op, "deferSyncH()");
3117                 scheduleSyncOperationH(op, delay);
3118             }
3119         }
3120 
3121         /* Same as deferSyncH, but assumes that job is no longer running on JobScheduler. */
deferStoppedSyncH(SyncOperation op, long delay)3122         private void deferStoppedSyncH(SyncOperation op, long delay) {
3123             if (op.isPeriodic) {
3124                 scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay);
3125             } else {
3126                 scheduleSyncOperationH(op, delay);
3127             }
3128         }
3129 
3130         /**
3131          * Cancel an active sync and reschedule it on the JobScheduler with some delay.
3132          */
deferActiveSyncH(ActiveSyncContext asc, String why)3133         private void deferActiveSyncH(ActiveSyncContext asc, String why) {
3134             SyncOperation op = asc.mSyncOperation;
3135             runSyncFinishedOrCanceledH(null, asc);
3136             deferSyncH(op, SYNC_DELAY_ON_CONFLICT, why);
3137         }
3138 
startSyncH(SyncOperation op)3139         private void startSyncH(SyncOperation op) {
3140             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
3141             if (isLoggable) Slog.v(TAG, op.toString());
3142 
3143             // At this point, we know the device has been connected to the server, so
3144             // assume the clock is correct.
3145             mSyncStorageEngine.setClockValid();
3146 
3147             SyncJobService.markSyncStarted(op.jobId);
3148 
3149             if (mStorageIsLow) {
3150                 deferSyncH(op, SYNC_DELAY_ON_LOW_STORAGE, "storage low");
3151                 return;
3152             }
3153 
3154             if (op.isPeriodic) {
3155                 // Don't allow this periodic to run if a previous instance failed and is currently
3156                 // scheduled according to some backoff criteria.
3157                 List<SyncOperation> ops = getAllPendingSyncs();
3158                 for (SyncOperation syncOperation: ops) {
3159                     if (syncOperation.sourcePeriodicId == op.jobId) {
3160                         SyncJobService.callJobFinished(op.jobId, false,
3161                                 "periodic sync, pending");
3162                         return;
3163                     }
3164                 }
3165                 // Don't allow this periodic to run if a previous instance failed and is currently
3166                 // executing according to some backoff criteria.
3167                 for (ActiveSyncContext asc: mActiveSyncContexts) {
3168                     if (asc.mSyncOperation.sourcePeriodicId == op.jobId) {
3169                         SyncJobService.callJobFinished(op.jobId, false,
3170                                 "periodic sync, already running");
3171                         return;
3172                     }
3173                 }
3174                 // Check for adapter delays.
3175                 if (isAdapterDelayed(op.target)) {
3176                     deferSyncH(op, 0 /* No minimum delay */, "backing off");
3177                     return;
3178                 }
3179             }
3180 
3181             // Check for conflicting syncs.
3182             for (ActiveSyncContext asc: mActiveSyncContexts) {
3183                 if (asc.mSyncOperation.isConflict(op)) {
3184                     // If the provided SyncOperation conflicts with a running one, the lower
3185                     // priority sync is pre-empted.
3186                     if (asc.mSyncOperation.findPriority() >= op.findPriority()) {
3187                         if (isLoggable) {
3188                             Slog.v(TAG, "Rescheduling sync due to conflict " + op.toString());
3189                         }
3190                         deferSyncH(op, SYNC_DELAY_ON_CONFLICT, "delay on conflict");
3191                         return;
3192                     } else {
3193                         if (isLoggable) {
3194                             Slog.v(TAG, "Pushing back running sync due to a higher priority sync");
3195                         }
3196                         deferActiveSyncH(asc, "preempted");
3197                         break;
3198                     }
3199                 }
3200             }
3201 
3202             final int syncOpState = computeSyncOpState(op);
3203             switch (syncOpState) {
3204                 case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS:
3205                 case SYNC_OP_STATE_INVALID: {
3206                     SyncJobService.callJobFinished(op.jobId, false,
3207                             "invalid op state: " + syncOpState);
3208                 } return;
3209             }
3210 
3211             if (!dispatchSyncOperation(op)) {
3212                 SyncJobService.callJobFinished(op.jobId, false, "dispatchSyncOperation() failed");
3213             }
3214 
3215             setAuthorityPendingState(op.target);
3216         }
3217 
findActiveSyncContextH(int jobId)3218         private ActiveSyncContext findActiveSyncContextH(int jobId) {
3219             for (ActiveSyncContext asc: mActiveSyncContexts) {
3220                 SyncOperation op = asc.mSyncOperation;
3221                 if (op != null && op.jobId == jobId) {
3222                     return asc;
3223                 }
3224             }
3225             return null;
3226         }
3227 
updateRunningAccountsH(EndPoint syncTargets)3228         private void updateRunningAccountsH(EndPoint syncTargets) {
3229             AccountAndUser[] oldAccounts = mRunningAccounts;
3230             mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
3231             if (Log.isLoggable(TAG, Log.VERBOSE)) {
3232                 Slog.v(TAG, "Accounts list: ");
3233                 for (AccountAndUser acc : mRunningAccounts) {
3234                     Slog.v(TAG, acc.toString());
3235                 }
3236             }
3237             if (mLogger.enabled()) {
3238                 mLogger.log("updateRunningAccountsH: ", Arrays.toString(mRunningAccounts));
3239             }
3240             removeStaleAccounts();
3241 
3242             AccountAndUser[] accounts = mRunningAccounts;
3243             for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
3244                 if (!containsAccountAndUser(accounts,
3245                         currentSyncContext.mSyncOperation.target.account,
3246                         currentSyncContext.mSyncOperation.target.userId)) {
3247                     Log.d(TAG, "canceling sync since the account is no longer running");
3248                     sendSyncFinishedOrCanceledMessage(currentSyncContext,
3249                             null /* no result since this is a cancel */);
3250                 }
3251             }
3252 
3253             if (syncTargets != null) {
3254                 // On account add, check if there are any settings to be restored.
3255                 for (AccountAndUser aau : mRunningAccounts) {
3256                     if (!containsAccountAndUser(oldAccounts, aau.account, aau.userId)) {
3257                         if (Log.isLoggable(TAG, Log.DEBUG)) {
3258                             Log.d(TAG, "Account " + aau.account
3259                                     + " added, checking sync restore data");
3260                         }
3261                         AccountSyncSettingsBackupHelper.accountAdded(mContext, syncTargets.userId);
3262                         break;
3263                     }
3264                 }
3265             }
3266 
3267             // Cancel all jobs from non-existent accounts.
3268             AccountAndUser[] allAccounts = AccountManagerService.getSingleton().getAllAccounts();
3269             List<SyncOperation> ops = getAllPendingSyncs();
3270             for (SyncOperation op: ops) {
3271                 if (!containsAccountAndUser(allAccounts, op.target.account, op.target.userId)) {
3272                     mLogger.log("canceling: ", op);
3273                     cancelJob(op, "updateRunningAccountsH()");
3274                 }
3275             }
3276 
3277             if (syncTargets != null) {
3278                 scheduleSync(syncTargets.account, syncTargets.userId,
3279                         SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider,
3280                         null, AuthorityInfo.NOT_INITIALIZED,
3281                         ContentResolver.SYNC_EXEMPTION_NONE, Process.myUid(), -4, null);
3282             }
3283         }
3284 
3285         /**
3286          * The given SyncOperation will be removed and a new one scheduled in its place if
3287          * an updated period or flex is specified.
3288          * @param syncOperation SyncOperation whose period and flex is to be updated.
3289          * @param pollFrequencyMillis new period in milliseconds.
3290          * @param flexMillis new flex time in milliseconds.
3291          */
maybeUpdateSyncPeriodH(SyncOperation syncOperation, long pollFrequencyMillis, long flexMillis)3292         private void maybeUpdateSyncPeriodH(SyncOperation syncOperation, long pollFrequencyMillis,
3293                 long flexMillis) {
3294             if (!(pollFrequencyMillis == syncOperation.periodMillis
3295                     && flexMillis == syncOperation.flexMillis)) {
3296                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3297                     Slog.v(TAG, "updating period " + syncOperation + " to " + pollFrequencyMillis
3298                             + " and flex to " + flexMillis);
3299                 }
3300                 SyncOperation newOp = new SyncOperation(syncOperation, pollFrequencyMillis,
3301                         flexMillis);
3302                 newOp.jobId = syncOperation.jobId;
3303                 scheduleSyncOperationH(newOp);
3304             }
3305         }
3306 
updateOrAddPeriodicSyncH(EndPoint target, long pollFrequency, long flex, Bundle extras)3307         private void updateOrAddPeriodicSyncH(EndPoint target, long pollFrequency, long flex,
3308                 Bundle extras) {
3309             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
3310             verifyJobScheduler();  // Will fill in mScheduledSyncs cache if it is not already filled.
3311             final long pollFrequencyMillis = pollFrequency * 1000L;
3312             final long flexMillis = flex * 1000L;
3313             if (isLoggable) {
3314                 Slog.v(TAG, "Addition to periodic syncs requested: " + target
3315                         + " period: " + pollFrequency
3316                         + " flexMillis: " + flex
3317                         + " extras: " + extras.toString());
3318             }
3319             List<SyncOperation> ops = getAllPendingSyncs();
3320             for (SyncOperation op: ops) {
3321                 if (op.isPeriodic && op.target.matchesSpec(target)
3322                         && syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) {
3323                     maybeUpdateSyncPeriodH(op, pollFrequencyMillis, flexMillis);
3324                     return;
3325                 }
3326             }
3327 
3328             if (isLoggable) {
3329                 Slog.v(TAG, "Adding new periodic sync: " + target
3330                         + " period: " + pollFrequency
3331                         + " flexMillis: " + flex
3332                         + " extras: " + extras.toString());
3333             }
3334 
3335             final RegisteredServicesCache.ServiceInfo<SyncAdapterType>
3336                     syncAdapterInfo = mSyncAdapters.getServiceInfo(
3337                     SyncAdapterType.newKey(
3338                             target.provider, target.account.type),
3339                     target.userId);
3340             if (syncAdapterInfo == null) {
3341                 return;
3342             }
3343 
3344             SyncOperation op = new SyncOperation(target, syncAdapterInfo.uid,
3345                     syncAdapterInfo.componentName.getPackageName(), SyncOperation.REASON_PERIODIC,
3346                     SyncStorageEngine.SOURCE_PERIODIC, extras,
3347                     syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID,
3348                     pollFrequencyMillis, flexMillis, ContentResolver.SYNC_EXEMPTION_NONE);
3349 
3350             final int syncOpState = computeSyncOpState(op);
3351             switch (syncOpState) {
3352                 case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS: {
3353                     String packageName = op.owningPackage;
3354                     final int userId = UserHandle.getUserId(op.owningUid);
3355                     // If the app did not run and has no account access, done
3356                     if (!wasPackageEverLaunched(packageName, userId)) {
3357                         return;
3358                     }
3359                     mAccountManagerInternal.requestAccountAccess(op.target.account,
3360                             packageName, userId, new RemoteCallback((Bundle result) -> {
3361                                 if (result != null
3362                                         && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
3363                                     updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
3364                                 }
3365                             }
3366                         ));
3367                 } return;
3368 
3369                 case SYNC_OP_STATE_INVALID: {
3370                     return;
3371                 }
3372             }
3373 
3374             scheduleSyncOperationH(op);
3375             mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
3376                     target.userId);
3377         }
3378 
3379         /**
3380          * Remove this periodic sync operation and all one-off operations initiated by it.
3381          */
removePeriodicSyncInternalH(SyncOperation syncOperation, String why)3382         private void removePeriodicSyncInternalH(SyncOperation syncOperation, String why) {
3383             // Remove this periodic sync and all one-off syncs initiated by it.
3384             List<SyncOperation> ops = getAllPendingSyncs();
3385             for (SyncOperation op: ops) {
3386                 if (op.sourcePeriodicId == syncOperation.jobId || op.jobId == syncOperation.jobId) {
3387                     ActiveSyncContext asc = findActiveSyncContextH(syncOperation.jobId);
3388                     if (asc != null) {
3389                         SyncJobService.callJobFinished(syncOperation.jobId, false,
3390                                 "removePeriodicSyncInternalH");
3391                         runSyncFinishedOrCanceledH(null, asc);
3392                     }
3393                     mLogger.log("removePeriodicSyncInternalH-canceling: ", op);
3394                     cancelJob(op, why);
3395                 }
3396             }
3397         }
3398 
removePeriodicSyncH(EndPoint target, Bundle extras, String why)3399         private void removePeriodicSyncH(EndPoint target, Bundle extras, String why) {
3400             verifyJobScheduler();
3401             List<SyncOperation> ops = getAllPendingSyncs();
3402             for (SyncOperation op: ops) {
3403                 if (op.isPeriodic && op.target.matchesSpec(target)
3404                         && syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) {
3405                     removePeriodicSyncInternalH(op, why);
3406                 }
3407             }
3408         }
3409 
isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext)3410         private boolean isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext) {
3411             final long bytesTransferredCurrent =
3412                     getTotalBytesTransferredByUid(activeSyncContext.mSyncAdapterUid);
3413             final long deltaBytesTransferred =
3414                     bytesTransferredCurrent - activeSyncContext.mBytesTransferredAtLastPoll;
3415 
3416             if (Log.isLoggable(TAG, Log.DEBUG)) {
3417                 // Bytes transferred
3418                 long remainder = deltaBytesTransferred;
3419                 long mb = remainder / (1024 * 1024);
3420                 remainder %= 1024 * 1024;
3421                 long kb = remainder / 1024;
3422                 remainder %= 1024;
3423                 long b = remainder;
3424                 Log.d(TAG, String.format(
3425                         "Time since last update: %ds. Delta transferred: %dMBs,%dKBs,%dBs",
3426                         (SystemClock.elapsedRealtime()
3427                                 - activeSyncContext.mLastPolledTimeElapsed)/1000,
3428                         mb, kb, b)
3429                 );
3430             }
3431             return (deltaBytesTransferred <= SYNC_MONITOR_PROGRESS_THRESHOLD_BYTES);
3432         }
3433 
3434         /**
3435          * Determine if a sync is no longer valid and should be dropped.
3436          */
computeSyncOpState(SyncOperation op)3437         private int computeSyncOpState(SyncOperation op) {
3438             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
3439             int state;
3440             final EndPoint target = op.target;
3441 
3442             // Drop the sync if the account of this operation no longer exists.
3443             AccountAndUser[] accounts = mRunningAccounts;
3444             if (!containsAccountAndUser(accounts, target.account, target.userId)) {
3445                 if (isLoggable) {
3446                     Slog.v(TAG, "    Dropping sync operation: account doesn't exist.");
3447                 }
3448                 return SYNC_OP_STATE_INVALID;
3449             }
3450             // Drop this sync request if it isn't syncable.
3451             state = computeSyncable(target.account, target.userId, target.provider, true);
3452             if (state == AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS) {
3453                 if (isLoggable) {
3454                     Slog.v(TAG, "    Dropping sync operation: "
3455                             + "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS");
3456                 }
3457                 return SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS;
3458             }
3459             if (state == AuthorityInfo.NOT_SYNCABLE) {
3460                 if (isLoggable) {
3461                     Slog.v(TAG, "    Dropping sync operation: isSyncable == NOT_SYNCABLE");
3462                 }
3463                 return SYNC_OP_STATE_INVALID;
3464             }
3465 
3466             final boolean syncEnabled = mSyncStorageEngine.getMasterSyncAutomatically(target.userId)
3467                     && mSyncStorageEngine.getSyncAutomatically(target.account,
3468                             target.userId, target.provider);
3469 
3470             // We ignore system settings that specify the sync is invalid if:
3471             // 1) It's manual - we try it anyway. When/if it fails it will be rescheduled.
3472             //      or
3473             // 2) it's an initialisation sync - we just need to connect to it.
3474             final boolean ignoreSystemConfiguration = op.isIgnoreSettings() || (state < 0);
3475 
3476             // Sync not enabled.
3477             if (!syncEnabled && !ignoreSystemConfiguration) {
3478                 if (isLoggable) {
3479                     Slog.v(TAG, "    Dropping sync operation: disallowed by settings/network.");
3480                 }
3481                 return SYNC_OP_STATE_INVALID;
3482             }
3483             return SYNC_OP_STATE_VALID;
3484         }
3485 
dispatchSyncOperation(SyncOperation op)3486         private boolean dispatchSyncOperation(SyncOperation op) {
3487             if (Log.isLoggable(TAG, Log.VERBOSE)) {
3488                 Slog.v(TAG, "dispatchSyncOperation: we are going to sync " + op);
3489                 Slog.v(TAG, "num active syncs: " + mActiveSyncContexts.size());
3490                 for (ActiveSyncContext syncContext : mActiveSyncContexts) {
3491                     Slog.v(TAG, syncContext.toString());
3492                 }
3493             }
3494             if (op.isAppStandbyExempted()) {
3495                 final UsageStatsManagerInternal usmi = LocalServices.getService(
3496                         UsageStatsManagerInternal.class);
3497                 if (usmi != null) {
3498                     usmi.reportExemptedSyncStart(op.owningPackage,
3499                             UserHandle.getUserId(op.owningUid));
3500                 }
3501             }
3502 
3503             // Connect to the sync adapter.
3504             int targetUid;
3505             ComponentName targetComponent;
3506             final SyncStorageEngine.EndPoint info = op.target;
3507             SyncAdapterType syncAdapterType =
3508                     SyncAdapterType.newKey(info.provider, info.account.type);
3509             final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
3510             syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, info.userId);
3511             if (syncAdapterInfo == null) {
3512                 mLogger.log("dispatchSyncOperation() failed: no sync adapter info for ",
3513                         syncAdapterType);
3514                 Log.d(TAG, "can't find a sync adapter for " + syncAdapterType
3515                         + ", removing settings for it");
3516                 mSyncStorageEngine.removeAuthority(info);
3517                 return false;
3518             }
3519             targetUid = syncAdapterInfo.uid;
3520             targetComponent = syncAdapterInfo.componentName;
3521             ActiveSyncContext activeSyncContext =
3522                     new ActiveSyncContext(op, insertStartSyncEvent(op), targetUid);
3523             if (Log.isLoggable(TAG, Log.VERBOSE)) {
3524                 Slog.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext);
3525             }
3526 
3527             activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext);
3528             mActiveSyncContexts.add(activeSyncContext);
3529 
3530             // Post message to begin monitoring this sync's progress.
3531             postMonitorSyncProgressMessage(activeSyncContext);
3532 
3533             if (!activeSyncContext.bindToSyncAdapter(targetComponent, info.userId)) {
3534                 mLogger.log("dispatchSyncOperation() failed: bind failed. target: ",
3535                         targetComponent);
3536                 Slog.e(TAG, "Bind attempt failed - target: " + targetComponent);
3537                 closeActiveSyncContext(activeSyncContext);
3538                 return false;
3539             }
3540 
3541             return true;
3542         }
3543 
runBoundToAdapterH(final ActiveSyncContext activeSyncContext, IBinder syncAdapter)3544         private void runBoundToAdapterH(final ActiveSyncContext activeSyncContext,
3545                 IBinder syncAdapter) {
3546             final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
3547             try {
3548                 activeSyncContext.mIsLinkedToDeath = true;
3549                 syncAdapter.linkToDeath(activeSyncContext, 0);
3550 
3551                 mLogger.log("Sync start: account=" + syncOperation.target.account,
3552                         " authority=", syncOperation.target.provider,
3553                         " reason=", SyncOperation.reasonToString(null, syncOperation.reason),
3554                         " extras=", SyncOperation.extrasToString(syncOperation.extras),
3555                         " adapter=", activeSyncContext.mSyncAdapter);
3556 
3557                 activeSyncContext.mSyncAdapter = ISyncAdapter.Stub.asInterface(syncAdapter);
3558                 activeSyncContext.mSyncAdapter
3559                         .startSync(activeSyncContext, syncOperation.target.provider,
3560                                 syncOperation.target.account, syncOperation.extras);
3561 
3562                 mLogger.log("Sync is running now...");
3563             } catch (RemoteException remoteExc) {
3564                 mLogger.log("Sync failed with RemoteException: ", remoteExc.toString());
3565                 Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc);
3566                 closeActiveSyncContext(activeSyncContext);
3567                 increaseBackoffSetting(syncOperation.target);
3568                 scheduleSyncOperationH(syncOperation);
3569             } catch (RuntimeException exc) {
3570                 mLogger.log("Sync failed with RuntimeException: ", exc.toString());
3571                 closeActiveSyncContext(activeSyncContext);
3572                 Slog.e(TAG, "Caught RuntimeException while starting the sync "
3573                         + logSafe(syncOperation), exc);
3574             }
3575         }
3576 
3577         /**
3578          * Cancel the sync for the provided target that matches the given bundle.
3579          * @param info Can have null fields to indicate all the active syncs for that field.
3580          * @param extras Can be null to indicate <strong>all</strong> syncs for the given endpoint.
3581          */
cancelActiveSyncH(SyncStorageEngine.EndPoint info, Bundle extras, String why)3582         private void cancelActiveSyncH(SyncStorageEngine.EndPoint info, Bundle extras,
3583                 String why) {
3584             ArrayList<ActiveSyncContext> activeSyncs =
3585                     new ArrayList<ActiveSyncContext>(mActiveSyncContexts);
3586             for (ActiveSyncContext activeSyncContext : activeSyncs) {
3587                 if (activeSyncContext != null) {
3588                     final SyncStorageEngine.EndPoint opInfo =
3589                             activeSyncContext.mSyncOperation.target;
3590                     if (!opInfo.matchesSpec(info)) {
3591                         continue;
3592                     }
3593                     if (extras != null &&
3594                             !syncExtrasEquals(activeSyncContext.mSyncOperation.extras,
3595                                     extras,
3596                                     false /* no config settings */)) {
3597                         continue;
3598                     }
3599                     SyncJobService.callJobFinished(activeSyncContext.mSyncOperation.jobId, false,
3600                             why);
3601                     runSyncFinishedOrCanceledH(null /* cancel => no result */, activeSyncContext);
3602                 }
3603             }
3604         }
3605 
3606         /**
3607          * Should be called when a one-off instance of a periodic sync completes successfully.
3608          */
reschedulePeriodicSyncH(SyncOperation syncOperation)3609         private void reschedulePeriodicSyncH(SyncOperation syncOperation) {
3610             // Ensure that the periodic sync wasn't removed.
3611             SyncOperation periodicSync = null;
3612             List<SyncOperation> ops = getAllPendingSyncs();
3613             for (SyncOperation op: ops) {
3614                 if (op.isPeriodic && syncOperation.matchesPeriodicOperation(op)) {
3615                     periodicSync = op;
3616                     break;
3617                 }
3618             }
3619             if (periodicSync == null) {
3620                 return;
3621             }
3622             scheduleSyncOperationH(periodicSync);
3623         }
3624 
runSyncFinishedOrCanceledH(SyncResult syncResult, ActiveSyncContext activeSyncContext)3625         private void runSyncFinishedOrCanceledH(SyncResult syncResult,
3626                 ActiveSyncContext activeSyncContext) {
3627             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
3628 
3629             final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
3630             final SyncStorageEngine.EndPoint info = syncOperation.target;
3631 
3632             if (activeSyncContext.mIsLinkedToDeath) {
3633                 activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0);
3634                 activeSyncContext.mIsLinkedToDeath = false;
3635             }
3636             final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime;
3637             String historyMessage;
3638             int downstreamActivity;
3639             int upstreamActivity;
3640 
3641             mLogger.log("runSyncFinishedOrCanceledH() op=", syncOperation, " result=", syncResult);
3642 
3643             if (syncResult != null) {
3644                 if (isLoggable) {
3645                     Slog.v(TAG, "runSyncFinishedOrCanceled [finished]: "
3646                             + syncOperation + ", result " + syncResult);
3647                 }
3648 
3649                 // In the non-canceled case, close the active sync context before doing the rest
3650                 // of the stuff.
3651                 closeActiveSyncContext(activeSyncContext);
3652 
3653                 // Note this part is probably okay to do before closeActiveSyncContext()...
3654                 // But moved here to restore OC-dev's behavior.  See b/64597061.
3655                 if (!syncOperation.isPeriodic) {
3656                     cancelJob(syncOperation, "runSyncFinishedOrCanceledH()-finished");
3657                 }
3658 
3659                 if (!syncResult.hasError()) {
3660                     historyMessage = SyncStorageEngine.MESG_SUCCESS;
3661                     // TODO: set these correctly when the SyncResult is extended to include it
3662                     downstreamActivity = 0;
3663                     upstreamActivity = 0;
3664                     clearBackoffSetting(syncOperation.target, "sync success");
3665 
3666                     // If the operation completes successfully and it was scheduled due to
3667                     // a periodic operation failing, we reschedule the periodic operation to
3668                     // start from now.
3669                     if (syncOperation.isDerivedFromFailedPeriodicSync()) {
3670                         reschedulePeriodicSyncH(syncOperation);
3671                     }
3672                 } else {
3673                     Log.w(TAG, "failed sync operation "
3674                             + logSafe(syncOperation) + ", " + syncResult);
3675 
3676                     syncOperation.retries++;
3677                     if (syncOperation.retries > mConstants.getMaxRetriesWithAppStandbyExemption()) {
3678                         syncOperation.syncExemptionFlag = ContentResolver.SYNC_EXEMPTION_NONE;
3679                     }
3680 
3681                     // the operation failed so increase the backoff time
3682                     increaseBackoffSetting(syncOperation.target);
3683                     if (!syncOperation.isPeriodic) {
3684                         // reschedule the sync if so indicated by the syncResult
3685                         maybeRescheduleSync(syncResult, syncOperation);
3686                     } else {
3687                         // create a normal sync instance that will respect adapter backoffs
3688                         postScheduleSyncMessage(syncOperation.createOneTimeSyncOperation(),
3689                                 0 /* min delay */);
3690                     }
3691                     historyMessage = ContentResolver.syncErrorToString(
3692                             syncResultToErrorNumber(syncResult));
3693                     // TODO: set these correctly when the SyncResult is extended to include it
3694                     downstreamActivity = 0;
3695                     upstreamActivity = 0;
3696                 }
3697                 setDelayUntilTime(syncOperation.target, syncResult.delayUntil);
3698             } else {
3699                 if (isLoggable) {
3700                     Slog.v(TAG, "runSyncFinishedOrCanceled [canceled]: " + syncOperation);
3701                 }
3702 
3703                 if (!syncOperation.isPeriodic) {
3704                     cancelJob(syncOperation, "runSyncFinishedOrCanceledH()-canceled");
3705                 }
3706 
3707                 if (activeSyncContext.mSyncAdapter != null) {
3708                     try {
3709                         mLogger.log("Calling cancelSync for runSyncFinishedOrCanceled ",
3710                                 activeSyncContext, "  adapter=", activeSyncContext.mSyncAdapter);
3711                         activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext);
3712                         mLogger.log("Canceled");
3713                     } catch (RemoteException e) {
3714                         mLogger.log("RemoteException ", Log.getStackTraceString(e));
3715                         // we don't need to retry this in this case
3716                     }
3717                 }
3718                 historyMessage = SyncStorageEngine.MESG_CANCELED;
3719                 downstreamActivity = 0;
3720                 upstreamActivity = 0;
3721 
3722                 // In the cancel sync case, close it after calling cancelSync().
3723                 closeActiveSyncContext(activeSyncContext);
3724             }
3725 
3726             stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage,
3727                     upstreamActivity, downstreamActivity, elapsedTime);
3728             // Check for full-resync and schedule it after closing off the last sync.
3729             if (syncResult != null && syncResult.tooManyDeletions) {
3730                 installHandleTooManyDeletesNotification(info.account,
3731                         info.provider, syncResult.stats.numDeletes,
3732                         info.userId);
3733             } else {
3734                 mNotificationMgr.cancelAsUser(
3735                         Integer.toString(info.account.hashCode() ^ info.provider.hashCode()),
3736                         SystemMessage.NOTE_SYNC_ERROR,
3737                         new UserHandle(info.userId));
3738             }
3739             if (syncResult != null && syncResult.fullSyncRequested) {
3740                 scheduleSyncOperationH(
3741                         new SyncOperation(info.account, info.userId,
3742                                 syncOperation.owningUid, syncOperation.owningPackage,
3743                                 syncOperation.reason,
3744                                 syncOperation.syncSource, info.provider, new Bundle(),
3745                                 syncOperation.allowParallelSyncs,
3746                                 syncOperation.syncExemptionFlag));
3747             }
3748         }
3749 
closeActiveSyncContext(ActiveSyncContext activeSyncContext)3750         private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) {
3751             activeSyncContext.close();
3752             mActiveSyncContexts.remove(activeSyncContext);
3753             mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo,
3754                     activeSyncContext.mSyncOperation.target.userId);
3755 
3756             if (Log.isLoggable(TAG, Log.VERBOSE)) {
3757                 Slog.v(TAG, "removing all MESSAGE_MONITOR_SYNC & MESSAGE_SYNC_EXPIRED for "
3758                         + activeSyncContext.toString());
3759             }
3760             mSyncHandler.removeMessages(SyncHandler.MESSAGE_MONITOR_SYNC, activeSyncContext);
3761 
3762             mLogger.log("closeActiveSyncContext: ", activeSyncContext);
3763         }
3764 
3765         /**
3766          * Convert the error-containing SyncResult into the Sync.History error number. Since
3767          * the SyncResult may indicate multiple errors at once, this method just returns the
3768          * most "serious" error.
3769          * @param syncResult the SyncResult from which to read
3770          * @return the most "serious" error set in the SyncResult
3771          * @throws IllegalStateException if the SyncResult does not indicate any errors.
3772          *   If SyncResult.error() is true then it is safe to call this.
3773          */
syncResultToErrorNumber(SyncResult syncResult)3774         private int syncResultToErrorNumber(SyncResult syncResult) {
3775             if (syncResult.syncAlreadyInProgress)
3776                 return ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
3777             if (syncResult.stats.numAuthExceptions > 0)
3778                 return ContentResolver.SYNC_ERROR_AUTHENTICATION;
3779             if (syncResult.stats.numIoExceptions > 0)
3780                 return ContentResolver.SYNC_ERROR_IO;
3781             if (syncResult.stats.numParseExceptions > 0)
3782                 return ContentResolver.SYNC_ERROR_PARSE;
3783             if (syncResult.stats.numConflictDetectedExceptions > 0)
3784                 return ContentResolver.SYNC_ERROR_CONFLICT;
3785             if (syncResult.tooManyDeletions)
3786                 return ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS;
3787             if (syncResult.tooManyRetries)
3788                 return ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES;
3789             if (syncResult.databaseError)
3790                 return ContentResolver.SYNC_ERROR_INTERNAL;
3791             throw new IllegalStateException("we are not in an error state, " + syncResult);
3792         }
3793 
installHandleTooManyDeletesNotification(Account account, String authority, long numDeletes, int userId)3794         private void installHandleTooManyDeletesNotification(Account account, String authority,
3795                 long numDeletes, int userId) {
3796             if (mNotificationMgr == null) return;
3797 
3798             final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider(
3799                     authority, 0 /* flags */);
3800             if (providerInfo == null) {
3801                 return;
3802             }
3803             CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager());
3804 
3805             Intent clickIntent = new Intent(mContext, SyncActivityTooManyDeletes.class);
3806             clickIntent.putExtra("account", account);
3807             clickIntent.putExtra("authority", authority);
3808             clickIntent.putExtra("provider", authorityName.toString());
3809             clickIntent.putExtra("numDeletes", numDeletes);
3810 
3811             if (!isActivityAvailable(clickIntent)) {
3812                 Log.w(TAG, "No activity found to handle too many deletes.");
3813                 return;
3814             }
3815 
3816             UserHandle user = new UserHandle(userId);
3817             final PendingIntent pendingIntent = PendingIntent
3818                     .getActivityAsUser(mContext, 0, clickIntent,
3819                             PendingIntent.FLAG_CANCEL_CURRENT, null, user);
3820 
3821             CharSequence tooManyDeletesDescFormat = mContext.getResources().getText(
3822                     R.string.contentServiceTooManyDeletesNotificationDesc);
3823 
3824             Context contextForUser = getContextForUser(user);
3825             Notification notification =
3826                     new Notification.Builder(contextForUser, SystemNotificationChannels.ACCOUNT)
3827                     .setSmallIcon(R.drawable.stat_notify_sync_error)
3828                     .setTicker(mContext.getString(R.string.contentServiceSync))
3829                     .setWhen(System.currentTimeMillis())
3830                     .setColor(contextForUser.getColor(
3831                             com.android.internal.R.color.system_notification_accent_color))
3832                     .setContentTitle(contextForUser.getString(
3833                             R.string.contentServiceSyncNotificationTitle))
3834                     .setContentText(
3835                             String.format(tooManyDeletesDescFormat.toString(), authorityName))
3836                     .setContentIntent(pendingIntent)
3837                     .build();
3838             notification.flags |= Notification.FLAG_ONGOING_EVENT;
3839             mNotificationMgr.notifyAsUser(
3840                     Integer.toString(account.hashCode() ^ authority.hashCode()),
3841                     SystemMessage.NOTE_SYNC_ERROR,
3842                     notification, user);
3843         }
3844 
3845         /**
3846          * Checks whether an activity exists on the system image for the given intent.
3847          *
3848          * @param intent The intent for an activity.
3849          * @return Whether or not an activity exists.
3850          */
isActivityAvailable(Intent intent)3851         private boolean isActivityAvailable(Intent intent) {
3852             PackageManager pm = mContext.getPackageManager();
3853             List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
3854             int listSize = list.size();
3855             for (int i = 0; i < listSize; i++) {
3856                 ResolveInfo resolveInfo = list.get(i);
3857                 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
3858                         != 0) {
3859                     return true;
3860                 }
3861             }
3862 
3863             return false;
3864         }
3865 
insertStartSyncEvent(SyncOperation syncOperation)3866         public long insertStartSyncEvent(SyncOperation syncOperation) {
3867             final long now = System.currentTimeMillis();
3868             EventLog.writeEvent(2720,
3869                     syncOperation.toEventLog(SyncStorageEngine.EVENT_START));
3870             return mSyncStorageEngine.insertStartSyncEvent(syncOperation, now);
3871         }
3872 
stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, int upstreamActivity, int downstreamActivity, long elapsedTime)3873         public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
3874                 int upstreamActivity, int downstreamActivity, long elapsedTime) {
3875             EventLog.writeEvent(2720,
3876                     syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP));
3877             mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
3878                     resultMessage, downstreamActivity, upstreamActivity,
3879                     syncOperation.target.userId);
3880         }
3881     }
3882 
isSyncStillActiveH(ActiveSyncContext activeSyncContext)3883     private boolean isSyncStillActiveH(ActiveSyncContext activeSyncContext) {
3884         for (ActiveSyncContext sync : mActiveSyncContexts) {
3885             if (sync == activeSyncContext) {
3886                 return true;
3887             }
3888         }
3889         return false;
3890     }
3891 
3892     /**
3893      * Sync extra comparison function.
3894      * @param b1 bundle to compare
3895      * @param b2 other bundle to compare
3896      * @param includeSyncSettings if false, ignore system settings in bundle.
3897      */
syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSyncSettings)3898     public static boolean syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSyncSettings) {
3899         if (b1 == b2) {
3900             return true;
3901         }
3902         // Exit early if we can.
3903         if (includeSyncSettings && b1.size() != b2.size()) {
3904             return false;
3905         }
3906         Bundle bigger = b1.size() > b2.size() ? b1 : b2;
3907         Bundle smaller = b1.size() > b2.size() ? b2 : b1;
3908         for (String key : bigger.keySet()) {
3909             if (!includeSyncSettings && isSyncSetting(key)) {
3910                 continue;
3911             }
3912             if (!smaller.containsKey(key)) {
3913                 return false;
3914             }
3915             if (!Objects.equals(bigger.get(key), smaller.get(key))) {
3916                 return false;
3917             }
3918         }
3919         return true;
3920     }
3921 
3922     /**
3923      * @return true if the provided key is used by the SyncManager in scheduling the sync.
3924      */
isSyncSetting(String key)3925     private static boolean isSyncSetting(String key) {
3926         if (key.equals(ContentResolver.SYNC_EXTRAS_EXPEDITED)) {
3927             return true;
3928         }
3929         if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS)) {
3930             return true;
3931         }
3932         if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF)) {
3933             return true;
3934         }
3935         if (key.equals(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY)) {
3936             return true;
3937         }
3938         if (key.equals(ContentResolver.SYNC_EXTRAS_MANUAL)) {
3939             return true;
3940         }
3941         if (key.equals(ContentResolver.SYNC_EXTRAS_UPLOAD)) {
3942             return true;
3943         }
3944         if (key.equals(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS)) {
3945             return true;
3946         }
3947         if (key.equals(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS)) {
3948             return true;
3949         }
3950         if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD)) {
3951             return true;
3952         }
3953         if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD)) {
3954             return true;
3955         }
3956         if (key.equals(ContentResolver.SYNC_EXTRAS_PRIORITY)) {
3957             return true;
3958         }
3959         if (key.equals(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED)) {
3960             return true;
3961         }
3962         if (key.equals(ContentResolver.SYNC_EXTRAS_INITIALIZE)) {
3963             return true;
3964         }
3965 //        if (key.equals(ContentResolver.SYNC_EXTRAS_APP_STANDBY_EXEMPTED)) {
3966 //            return true;
3967 //        }
3968         // No need to check virtual flags such as SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC.
3969         return false;
3970     }
3971 
3972     static class PrintTable {
3973         private ArrayList<String[]> mTable = Lists.newArrayList();
3974         private final int mCols;
3975 
PrintTable(int cols)3976         PrintTable(int cols) {
3977             mCols = cols;
3978         }
3979 
set(int row, int col, Object... values)3980         void set(int row, int col, Object... values) {
3981             if (col + values.length > mCols) {
3982                 throw new IndexOutOfBoundsException("Table only has " + mCols +
3983                         " columns. can't set " + values.length + " at column " + col);
3984             }
3985             for (int i = mTable.size(); i <= row; i++) {
3986                 final String[] list = new String[mCols];
3987                 mTable.add(list);
3988                 for (int j = 0; j < mCols; j++) {
3989                     list[j] = "";
3990                 }
3991             }
3992             final String[] rowArray = mTable.get(row);
3993             for (int i = 0; i < values.length; i++) {
3994                 final Object value = values[i];
3995                 rowArray[col + i] = (value == null) ? "" : value.toString();
3996             }
3997         }
3998 
writeTo(PrintWriter out)3999         void writeTo(PrintWriter out) {
4000             final String[] formats = new String[mCols];
4001             int totalLength = 0;
4002             for (int col = 0; col < mCols; ++col) {
4003                 int maxLength = 0;
4004                 for (Object[] row : mTable) {
4005                     final int length = row[col].toString().length();
4006                     if (length > maxLength) {
4007                         maxLength = length;
4008                     }
4009                 }
4010                 totalLength += maxLength;
4011                 formats[col] = String.format("%%-%ds", maxLength);
4012             }
4013             formats[mCols - 1] = "%s";
4014             printRow(out, formats, mTable.get(0));
4015             totalLength += (mCols - 1) * 2;
4016             for (int i = 0; i < totalLength; ++i) {
4017                 out.print("-");
4018             }
4019             out.println();
4020             for (int i = 1, mTableSize = mTable.size(); i < mTableSize; i++) {
4021                 Object[] row = mTable.get(i);
4022                 printRow(out, formats, row);
4023             }
4024         }
4025 
printRow(PrintWriter out, String[] formats, Object[] row)4026         private void printRow(PrintWriter out, String[] formats, Object[] row) {
4027             for (int j = 0, rowLength = row.length; j < rowLength; j++) {
4028                 out.printf(String.format(formats[j], row[j].toString()));
4029                 out.print("  ");
4030             }
4031             out.println();
4032         }
4033 
getNumRows()4034         public int getNumRows() {
4035             return mTable.size();
4036         }
4037     }
4038 
getContextForUser(UserHandle user)4039     private Context getContextForUser(UserHandle user) {
4040         try {
4041             return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
4042         } catch (NameNotFoundException e) {
4043             // Default to mContext, not finding the package system is running as is unlikely.
4044             return mContext;
4045         }
4046     }
4047 
cancelJob(SyncOperation op, String why)4048     private void cancelJob(SyncOperation op, String why) {
4049         if (op == null) {
4050             Slog.wtf(TAG, "Null sync operation detected.");
4051             return;
4052         }
4053         if (op.isPeriodic) {
4054             mLogger.log("Removing periodic sync ", op, " for ", why);
4055         }
4056         getJobScheduler().cancel(op.jobId);
4057     }
4058 
resetTodayStats()4059     public void resetTodayStats() {
4060         mSyncStorageEngine.resetTodayStats(/*force=*/ true);
4061     }
4062 
wasPackageEverLaunched(String packageName, int userId)4063     private boolean wasPackageEverLaunched(String packageName, int userId) {
4064         try {
4065             return mPackageManagerInternal.wasPackageEverLaunched(packageName, userId);
4066         } catch (IllegalArgumentException e) {
4067             return false; // Package has been removed.
4068         }
4069     }
4070 }
4071