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