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