Clean up sync manager and fix the lock screen blocked sync issue.

- Fix the issue where when a sync starts when the user is locked, the sync
is enqueued and waits for the 10 minute timeout, which drains the battery.

Now, in this case, we finished the job right away, and if it's a non-periodic
sync, we ask the job scheduler to reschedule.

- Clean up all the mess and unnecessary code.

Fixes: 79433653
Test: manual: Add an account, remove it, make sure all the sync operations are
gone from dumpsys content.
Test: manual: Use the requestsync command while the user is locked and
make sure the sync won't stuck.

Change-Id: I8cfd6a80715336ebea3793b2ed10b59d90cd8e52
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
index 089632d..bfcc629 100644
--- a/services/core/java/com/android/server/content/SyncJobService.java
+++ b/services/core/java/com/android/server/content/SyncJobService.java
@@ -16,12 +16,10 @@
 
 package com.android.server.content;
 
+import android.annotation.Nullable;
 import android.app.job.JobParameters;
 import android.app.job.JobService;
-import android.content.Intent;
 import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Log;
 import android.util.Slog;
@@ -34,78 +32,86 @@
 public class SyncJobService extends JobService {
     private static final String TAG = "SyncManager";
 
-    public static final String EXTRA_MESSENGER = "messenger";
+    private static final Object sLock = new Object();
 
-    private Messenger mMessenger;
+    @GuardedBy("sLock")
+    private static SyncJobService sInstance;
 
-    private final Object mLock = new Object();
+    @GuardedBy("sLock")
+    private static final SparseArray<JobParameters> sJobParamsMap = new SparseArray<>();
 
-    @GuardedBy("mLock")
-    private final SparseArray<JobParameters> mJobParamsMap = new SparseArray<>();
+    @GuardedBy("sLock")
+    private static final SparseBooleanArray sStartedSyncs = new SparseBooleanArray();
 
-    @GuardedBy("mLock")
-    private final SparseBooleanArray mStartedSyncs = new SparseBooleanArray();
+    @GuardedBy("sLock")
+    private static final SparseLongArray sJobStartUptimes = new SparseLongArray();
 
-    @GuardedBy("mLock")
-    private final SparseLongArray mJobStartUptimes = new SparseLongArray();
+    private static final SyncLogger sLogger = SyncLogger.getInstance();
 
-    private final SyncLogger mLogger = SyncLogger.getInstance();
-
-    /**
-     * This service is started by the SyncManager which passes a messenger object to
-     * communicate back with it. It never stops while the device is running.
-     */
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        mMessenger = intent.getParcelableExtra(EXTRA_MESSENGER);
-        Message m = Message.obtain();
-        m.what = SyncManager.SyncHandler.MESSAGE_JOBSERVICE_OBJECT;
-        m.obj = this;
-        sendMessage(m);
-
-        return START_NOT_STICKY;
+    private void updateInstance() {
+        synchronized (SyncJobService.class) {
+            sInstance = this;
+        }
     }
 
-    private void sendMessage(Message message) {
-        if (mMessenger == null) {
-            Slog.e(TAG, "Messenger not initialized.");
-            return;
+    @Nullable
+    private static SyncJobService getInstance() {
+        synchronized (sLock) {
+            if (sInstance == null) {
+                Slog.wtf(TAG, "sInstance == null");
+            }
+            return sInstance;
         }
-        try {
-            mMessenger.send(message);
-        } catch (RemoteException e) {
-            Slog.e(TAG, e.toString());
+    }
+
+    public static boolean isReady() {
+        synchronized (sLock) {
+            return sInstance != null;
         }
     }
 
     @Override
     public boolean onStartJob(JobParameters params) {
+        updateInstance();
 
-        mLogger.purgeOldLogs();
+        sLogger.purgeOldLogs();
+
+        SyncOperation op = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
+
+        if (op == null) {
+            Slog.wtf(TAG, "Got invalid job " + params.getJobId());
+            return false;
+        }
+
+        final boolean readyToSync = SyncManager.readyToSync(op.target.userId);
+
+        sLogger.log("onStartJob() jobid=", params.getJobId(), " op=", op,
+                " readyToSync", readyToSync);
+
+        if (!readyToSync) {
+            // If the user isn't unlocked or the device has been provisioned yet, just stop the job
+            // at this point. If it's a non-periodic sync, ask the job scheduler to reschedule it.
+            // If it's a periodic sync, then just wait until the next cycle.
+            final boolean wantsReschedule = !op.isPeriodic;
+            jobFinished(params, wantsReschedule);
+            return true;
+        }
 
         boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
-        synchronized (mLock) {
+        synchronized (sLock) {
             final int jobId = params.getJobId();
-            mJobParamsMap.put(jobId, params);
+            sJobParamsMap.put(jobId, params);
 
-            mStartedSyncs.delete(jobId);
-            mJobStartUptimes.put(jobId, SystemClock.uptimeMillis());
+            sStartedSyncs.delete(jobId);
+            sJobStartUptimes.put(jobId, SystemClock.uptimeMillis());
         }
         Message m = Message.obtain();
         m.what = SyncManager.SyncHandler.MESSAGE_START_SYNC;
-        SyncOperation op = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
-
-        mLogger.log("onStartJob() jobid=", params.getJobId(), " op=", op);
-
-        if (op == null) {
-            Slog.e(TAG, "Got invalid job " + params.getJobId());
-            return false;
-        }
         if (isLoggable) {
             Slog.v(TAG, "Got start job message " + op.target);
         }
         m.obj = op;
-        sendMessage(m);
+        SyncManager.sendMessage(m);
         return true;
     }
 
@@ -115,15 +121,22 @@
             Slog.v(TAG, "onStopJob called " + params.getJobId() + ", reason: "
                     + params.getStopReason());
         }
-        final boolean readyToSync = SyncManager.readyToSync();
+        final SyncOperation op = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
+        if (op == null) {
+            Slog.wtf(TAG, "Got invalid job " + params.getJobId());
+            return false;
+        }
 
-        mLogger.log("onStopJob() ", mLogger.jobParametersToString(params),
+        final boolean readyToSync = SyncManager.readyToSync(op.target.userId);
+
+        sLogger.log("onStopJob() ", sLogger.jobParametersToString(params),
                 " readyToSync=", readyToSync);
-        synchronized (mLock) {
-            final int jobId = params.getJobId();
-            mJobParamsMap.remove(jobId);
 
-            final long startUptime = mJobStartUptimes.get(jobId);
+        synchronized (sLock) {
+            final int jobId = params.getJobId();
+            sJobParamsMap.remove(jobId);
+
+            final long startUptime = sJobStartUptimes.get(jobId);
             final long nowUptime = SystemClock.uptimeMillis();
             final long runtime = nowUptime - startUptime;
 
@@ -135,61 +148,57 @@
                 // WTF if startSyncH() hasn't happened, *unless* onStopJob() was called too soon.
                 // (1 minute threshold.)
                 // Also don't wtf when it's not ready to sync.
-                if (readyToSync && !mStartedSyncs.get(jobId)) {
+                if (readyToSync && !sStartedSyncs.get(jobId)) {
                     wtf("Job " + jobId + " didn't start: "
                             + " startUptime=" + startUptime
                             + " nowUptime=" + nowUptime
                             + " params=" + jobParametersToString(params));
                 }
-            } else if (runtime < 10 * 1000) {
-                // This happens too in a normal case too, and it's rather too often.
-                // Disable it for now.
-//                // Job stopped too soon. WTF.
-//                wtf("Job " + jobId + " stopped in " + runtime + " ms: "
-//                        + " startUptime=" + startUptime
-//                        + " nowUptime=" + nowUptime
-//                        + " params=" + jobParametersToString(params));
             }
 
-            mStartedSyncs.delete(jobId);
-            mJobStartUptimes.delete(jobId);
+            sStartedSyncs.delete(jobId);
+            sJobStartUptimes.delete(jobId);
         }
         Message m = Message.obtain();
         m.what = SyncManager.SyncHandler.MESSAGE_STOP_SYNC;
-        m.obj = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
-        if (m.obj == null) {
-            return false;
-        }
+        m.obj = op;
 
         // Reschedule if this job was NOT explicitly canceled.
         m.arg1 = params.getStopReason() != JobParameters.REASON_CANCELED ? 1 : 0;
         // Apply backoff only if stop is called due to timeout.
         m.arg2 = params.getStopReason() == JobParameters.REASON_TIMEOUT ? 1 : 0;
 
-        sendMessage(m);
+        SyncManager.sendMessage(m);
         return false;
     }
 
-    public void callJobFinished(int jobId, boolean needsReschedule, String why) {
-        synchronized (mLock) {
-            JobParameters params = mJobParamsMap.get(jobId);
-            mLogger.log("callJobFinished()",
+    public static void callJobFinished(int jobId, boolean needsReschedule, String why) {
+        final SyncJobService instance = getInstance();
+        if (instance != null) {
+            instance.callJobFinishedInner(jobId, needsReschedule, why);
+        }
+    }
+
+    public void callJobFinishedInner(int jobId, boolean needsReschedule, String why) {
+        synchronized (sLock) {
+            JobParameters params = sJobParamsMap.get(jobId);
+            sLogger.log("callJobFinished()",
                     " jobid=", jobId,
                     " needsReschedule=", needsReschedule,
-                    " ", mLogger.jobParametersToString(params),
+                    " ", sLogger.jobParametersToString(params),
                     " why=", why);
             if (params != null) {
                 jobFinished(params, needsReschedule);
-                mJobParamsMap.remove(jobId);
+                sJobParamsMap.remove(jobId);
             } else {
                 Slog.e(TAG, "Job params not found for " + String.valueOf(jobId));
             }
         }
     }
 
-    public void markSyncStarted(int jobId) {
-        synchronized (mLock) {
-            mStartedSyncs.put(jobId, true);
+    public static void markSyncStarted(int jobId) {
+        synchronized (sLock) {
+            sStartedSyncs.put(jobId, true);
         }
     }
 
@@ -203,8 +212,8 @@
         }
     }
 
-    private void wtf(String message) {
-        mLogger.log(message);
+    private static void wtf(String message) {
+        sLogger.log(message);
         Slog.wtf(TAG, message);
     }
 }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 0a640b8..d06e785 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -21,6 +21,7 @@
 import android.accounts.AccountManager;
 import android.accounts.AccountManagerInternal;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
@@ -72,7 +73,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.Messenger;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteCallback;
@@ -89,6 +89,7 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
+import android.util.SparseBooleanArray;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -227,7 +228,6 @@
     // TODO: add better locking around mRunningAccounts
     private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY;
 
-    volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
     volatile private PowerManager.WakeLock mSyncManagerWakeLock;
     volatile private boolean mDataConnectionIsConnected = false;
     volatile private boolean mStorageIsLow = false;
@@ -238,7 +238,6 @@
     private final IBatteryStats mBatteryStats;
     private JobScheduler mJobScheduler;
     private JobSchedulerInternal mJobSchedulerInternal;
-    private SyncJobService mSyncJobService;
 
     private SyncStorageEngine mSyncStorageEngine;
 
@@ -318,16 +317,6 @@
                 }
             };
 
-    private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            mBootCompleted = true;
-            // Called because it gets all pending jobs and stores them in mScheduledSyncs cache.
-            verifyJobScheduler();
-            mSyncHandler.onBootCompleted();
-        }
-    };
-
     private final BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -371,14 +360,14 @@
         m.sendToTarget();
     }
 
-    private void doDatabaseCleanup() {
+    private void removeStaleAccounts() {
         for (UserInfo user : mUserManager.getUsers(true)) {
             // Skip any partially created/removed users
             if (user.partial) continue;
             Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(
                     user.id, mContext.getOpPackageName());
 
-            mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id);
+            mSyncStorageEngine.removeStaleAccounts(accountsForUser, user.id);
         }
     }
 
@@ -464,8 +453,8 @@
     private final SyncHandler mSyncHandler;
     private final SyncManagerConstants mConstants;
 
-    private volatile boolean mBootCompleted = false;
-    private volatile boolean mJobServiceReady = false;
+    @GuardedBy("mUnlockedUsers")
+    private final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray();
 
     private ConnectivityManager getConnectivityManager() {
         synchronized (this) {
@@ -641,12 +630,6 @@
         IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
         context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
 
-        if (!factoryTest) {
-            intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
-            intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-            context.registerReceiver(mBootCompletedReceiver, intentFilter);
-        }
-
         intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
         intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
         context.registerReceiver(mStorageIntentReceiver, intentFilter);
@@ -690,14 +673,6 @@
         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
                 BatteryStats.SERVICE_NAME));
 
-        // This WakeLock is used to ensure that we stay awake between the time that we receive
-        // a sync alarm notification and when we finish processing it. We need to do this
-        // because we don't do the work in the alarm handler, rather we do it in a message
-        // handler.
-        mHandleAlarmWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
-                HANDLE_SYNC_ALARM_WAKE_LOCK);
-        mHandleAlarmWakeLock.setReferenceCounted(false);
-
         // This WakeLock is used to ensure that we stay awake while running the sync loop
         // message handler. Normally we will hold a sync adapter wake lock while it is being
         // synced but during the execution of the sync loop it might finish a sync for
@@ -715,7 +690,6 @@
                         public void onChange(boolean selfChange) {
                             mProvisioned |= isDeviceProvisioned();
                             if (mProvisioned) {
-                                mSyncHandler.onDeviceProvisioned();
                                 resolver.unregisterContentObserver(this);
                             }
                         }
@@ -744,19 +718,6 @@
                     null, null);
         }
 
-        // Set up the communication channel between the scheduled job and the sync manager.
-        // This is posted to the *main* looper intentionally, to defer calling startService()
-        // until after the lengthy primary boot sequence completes on that thread, to avoid
-        // spurious ANR triggering.
-        final Intent startServiceIntent = new Intent(mContext, SyncJobService.class);
-        startServiceIntent.putExtra(SyncJobService.EXTRA_MESSENGER, new Messenger(mSyncHandler));
-        new Handler(mContext.getMainLooper()).post(new Runnable() {
-            @Override
-            public void run() {
-                mContext.startService(startServiceIntent);
-            }
-        });
-
         // Sync adapters were able to access the synced account without the accounts
         // permission which circumvents our permission model. Therefore, we require
         // sync adapters that don't have access to the account to get user consent.
@@ -768,16 +729,31 @@
         mLogger.log("Sync manager initialized: " + Build.FINGERPRINT);
     }
 
-    public void onStartUser(int userHandle) {
-        mSyncHandler.post(() -> mLogger.log("onStartUser: user=", userHandle));
+    public void onStartUser(int userId) {
+        // Log on the handler to avoid slowing down device boot.
+        mSyncHandler.post(() -> mLogger.log("onStartUser: user=", userId));
     }
 
-    public void onUnlockUser(int userHandle) {
-        mSyncHandler.post(() -> mLogger.log("onUnlockUser: user=", userHandle));
+    public void onUnlockUser(int userId) {
+        synchronized (mUnlockedUsers) {
+            mUnlockedUsers.put(userId, true);
+        }
+        // Log on the handler to avoid slowing down device boot.
+        mSyncHandler.post(() -> mLogger.log("onUnlockUser: user=", userId));
     }
 
-    public void onStopUser(int userHandle) {
-        mSyncHandler.post(() -> mLogger.log("onStopUser: user=", userHandle));
+    public void onStopUser(int userId) {
+        synchronized (mUnlockedUsers) {
+            mUnlockedUsers.put(userId, false);
+        }
+        // Log on the handler to avoid slowing down user switch.
+        mSyncHandler.post(() -> mLogger.log("onStopUser: user=", userId));
+    }
+
+    private boolean isUserUnlocked(int userId) {
+        synchronized (mUnlockedUsers) {
+            return mUnlockedUsers.get(userId);
+        }
     }
 
     public void onBootPhase(int phase) {
@@ -1820,7 +1796,7 @@
         updateRunningAccounts(null /* Don't sync any target */);
 
         // Clean up the storage engine database
-        mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId);
+        mSyncStorageEngine.removeStaleAccounts(null, userId);
         List<SyncOperation> ops = getAllPendingSyncs();
         for (SyncOperation op: ops) {
             if (op.target.userId == userId) {
@@ -2235,8 +2211,13 @@
         mSyncStorageEngine.resetTodayStats(/* force=*/ false);
 
         for (AccountAndUser account : accounts) {
-            pw.printf("Account %s u%d %s\n",
-                    account.account.name, account.userId, account.account.type);
+            final boolean unlocked;
+            synchronized (mUnlockedUsers) {
+                unlocked = mUnlockedUsers.get(account.userId);
+            }
+            pw.printf("Account %s u%d %s%s\n",
+                    account.account.name, account.userId, account.account.type,
+                    (unlocked ? "" : " (locked)"));
 
             pw.println("=======================================================================");
             final PrintTable table = new PrintTable(16);
@@ -2872,13 +2853,29 @@
         }
     }
 
-    /**
-     * @return whether the device is ready to run sync jobs.
-     */
-    public static boolean readyToSync() {
+    @Nullable
+    private static SyncManager getInstance() {
         synchronized (SyncManager.class) {
-            return sInstance != null && sInstance.mProvisioned && sInstance.mBootCompleted
-                    && sInstance.mJobServiceReady;
+            if (sInstance == null) {
+                Slog.wtf(TAG, "sInstance == null"); // Maybe called too early?
+            }
+            return sInstance;
+        }
+    }
+
+    /**
+     * @return whether the device is ready to run sync jobs for a given user.
+     */
+    public static boolean readyToSync(int userId) {
+        final SyncManager instance = getInstance();
+        return (instance != null) && SyncJobService.isReady()
+                && instance.mProvisioned && instance.isUserUnlocked(userId);
+    }
+
+    public static void sendMessage(Message message) {
+        final SyncManager instance = getInstance();
+        if (instance != null) {
+            instance.mSyncHandler.sendMessage(message);
         }
     }
 
@@ -2889,11 +2886,9 @@
     class SyncHandler extends Handler {
         // Messages that can be sent on mHandler.
         private static final int MESSAGE_SYNC_FINISHED = 1;
-        private static final int MESSAGE_RELEASE_MESSAGES_FROM_QUEUE = 2;
         private static final int MESSAGE_SERVICE_CONNECTED = 4;
         private static final int MESSAGE_SERVICE_DISCONNECTED = 5;
         private static final int MESSAGE_CANCEL = 6;
-        static final int MESSAGE_JOBSERVICE_OBJECT = 7;
         static final int MESSAGE_START_SYNC = 10;
         static final int MESSAGE_STOP_SYNC = 11;
         static final int MESSAGE_SCHEDULE_SYNC = 12;
@@ -2910,86 +2905,17 @@
         public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker();
         private final HashMap<String, PowerManager.WakeLock> mWakeLocks = Maps.newHashMap();
 
-        private List<Message> mUnreadyQueue = new ArrayList<Message>();
-
-        void onBootCompleted() {
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                Slog.v(TAG, "Boot completed.");
-            }
-            checkIfDeviceReady();
-        }
-
-        void onDeviceProvisioned() {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "mProvisioned=" + mProvisioned);
-            }
-            checkIfDeviceReady();
-        }
-
-        void checkIfDeviceReady() {
-            if (mProvisioned && mBootCompleted && mJobServiceReady) {
-                synchronized(this) {
-                    mSyncStorageEngine.restoreAllPeriodicSyncs();
-                    // Dispatch any stashed messages.
-                    obtainMessage(MESSAGE_RELEASE_MESSAGES_FROM_QUEUE).sendToTarget();
-                }
-            }
-        }
-
-        /**
-         * Stash any messages that come to the handler before boot is complete or before the device
-         * is properly provisioned (i.e. out of set-up wizard).
-         * {@link #onBootCompleted()} and {@link SyncHandler#onDeviceProvisioned} both
-         * need to come in before we start syncing.
-         * @param msg Message to dispatch at a later point.
-         * @return true if a message was enqueued, false otherwise. This is to avoid losing the
-         * message if we manage to acquire the lock but by the time we do boot has completed.
-         */
-        private boolean tryEnqueueMessageUntilReadyToRun(Message msg) {
-            synchronized (this) {
-                if (!mBootCompleted || !mProvisioned || !mJobServiceReady) {
-                    // Need to copy the message bc looper will recycle it.
-                    Message m = Message.obtain(msg);
-                    mUnreadyQueue.add(m);
-                    return true;
-                } else {
-                    return false;
-                }
-            }
-        }
-
         public SyncHandler(Looper looper) {
             super(looper);
         }
 
         public void handleMessage(Message msg) {
+            // TODO Do we really need this wake lock?? If we actually needed it, this is probably
+            // not the best place to acquire the lock -- it's probably too late, because the device
+            // could have gone to sleep before we reach here.
+            mSyncManagerWakeLock.acquire();
             try {
-                mSyncManagerWakeLock.acquire();
-                // We only want to enqueue sync related messages until device is ready.
-                // Other messages are handled without enqueuing.
-                if (msg.what == MESSAGE_JOBSERVICE_OBJECT) {
-                    Slog.i(TAG, "Got SyncJobService instance.");
-                    mSyncJobService = (SyncJobService) msg.obj;
-                    mJobServiceReady = true;
-                    checkIfDeviceReady();
-                } else if (msg.what == SyncHandler.MESSAGE_ACCOUNTS_UPDATED) {
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Slog.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED");
-                    }
-                    EndPoint targets = (EndPoint) msg.obj;
-                    updateRunningAccountsH(targets);
-                } else if (msg.what == MESSAGE_RELEASE_MESSAGES_FROM_QUEUE) {
-                    if (mUnreadyQueue != null) {
-                        for (Message m : mUnreadyQueue) {
-                            handleSyncMessage(m);
-                        }
-                        mUnreadyQueue = null;
-                    }
-                } else if (tryEnqueueMessageUntilReadyToRun(msg)) {
-                    // No work to be done.
-                } else {
-                    handleSyncMessage(msg);
-                }
+                handleSyncMessage(msg);
             } finally {
                 mSyncManagerWakeLock.release();
             }
@@ -3001,6 +2927,13 @@
             try {
                 mDataConnectionIsConnected = readDataConnectionState();
                 switch (msg.what) {
+                    case MESSAGE_ACCOUNTS_UPDATED:
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Slog.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED");
+                        }
+                        EndPoint targets = (EndPoint) msg.obj;
+                        updateRunningAccountsH(targets);
+                        break;
                     case MESSAGE_SCHEDULE_SYNC:
                         ScheduleSyncMessagePayload syncPayload =
                                 (ScheduleSyncMessagePayload) msg.obj;
@@ -3069,7 +3002,7 @@
                         if (isLoggable) {
                             Slog.v(TAG, "syncFinished" + payload.activeSyncContext.mSyncOperation);
                         }
-                        mSyncJobService.callJobFinished(
+                        SyncJobService.callJobFinished(
                                 payload.activeSyncContext.mSyncOperation.jobId, false,
                                 "sync finished");
                         runSyncFinishedOrCanceledH(payload.syncResult,
@@ -3119,7 +3052,7 @@
                             // which is a soft error.
                             SyncResult syncResult = new SyncResult();
                             syncResult.stats.numIoExceptions++;
-                            mSyncJobService.callJobFinished(
+                            SyncJobService.callJobFinished(
                                     currentSyncContext.mSyncOperation.jobId, false,
                                     "service disconnected");
                             runSyncFinishedOrCanceledH(syncResult, currentSyncContext);
@@ -3138,7 +3071,7 @@
                             Log.w(TAG, String.format(
                                     "Detected sync making no progress for %s. cancelling.",
                                     monitoredSyncContext));
-                            mSyncJobService.callJobFinished(
+                            SyncJobService.callJobFinished(
                                     monitoredSyncContext.mSyncOperation.jobId, false,
                                     "no network activity");
                             runSyncFinishedOrCanceledH(
@@ -3175,7 +3108,7 @@
         private void deferSyncH(SyncOperation op, long delay, String why) {
             mLogger.log("deferSyncH() ", (op.isPeriodic ? "periodic " : ""),
                     "sync.  op=", op, " delay=", delay, " why=", why);
-            mSyncJobService.callJobFinished(op.jobId, false, why);
+            SyncJobService.callJobFinished(op.jobId, false, why);
             if (op.isPeriodic) {
                 scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay);
             } else {
@@ -3213,7 +3146,7 @@
             // assume the clock is correct.
             mSyncStorageEngine.setClockValid();
 
-            mSyncJobService.markSyncStarted(op.jobId);
+            SyncJobService.markSyncStarted(op.jobId);
 
             if (mStorageIsLow) {
                 deferSyncH(op, SYNC_DELAY_ON_LOW_STORAGE, "storage low");
@@ -3226,7 +3159,7 @@
                 List<SyncOperation> ops = getAllPendingSyncs();
                 for (SyncOperation syncOperation: ops) {
                     if (syncOperation.sourcePeriodicId == op.jobId) {
-                        mSyncJobService.callJobFinished(op.jobId, false,
+                        SyncJobService.callJobFinished(op.jobId, false,
                                 "periodic sync, pending");
                         return;
                     }
@@ -3235,7 +3168,7 @@
                 // executing according to some backoff criteria.
                 for (ActiveSyncContext asc: mActiveSyncContexts) {
                     if (asc.mSyncOperation.sourcePeriodicId == op.jobId) {
-                        mSyncJobService.callJobFinished(op.jobId, false,
+                        SyncJobService.callJobFinished(op.jobId, false,
                                 "periodic sync, already running");
                         return;
                     }
@@ -3272,13 +3205,13 @@
             switch (syncOpState) {
                 case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS:
                 case SYNC_OP_STATE_INVALID: {
-                    mSyncJobService.callJobFinished(op.jobId, false,
+                    SyncJobService.callJobFinished(op.jobId, false,
                             "invalid op state: " + syncOpState);
                 } return;
             }
 
             if (!dispatchSyncOperation(op)) {
-                mSyncJobService.callJobFinished(op.jobId, false, "dispatchSyncOperation() failed");
+                SyncJobService.callJobFinished(op.jobId, false, "dispatchSyncOperation() failed");
             }
 
             setAuthorityPendingState(op.target);
@@ -3306,9 +3239,7 @@
             if (mLogger.enabled()) {
                 mLogger.log("updateRunningAccountsH: ", Arrays.toString(mRunningAccounts));
             }
-            if (mBootCompleted) {
-                doDatabaseCleanup();
-            }
+            removeStaleAccounts();
 
             AccountAndUser[] accounts = mRunningAccounts;
             for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
@@ -3453,7 +3384,7 @@
                 if (op.sourcePeriodicId == syncOperation.jobId || op.jobId == syncOperation.jobId) {
                     ActiveSyncContext asc = findActiveSyncContextH(syncOperation.jobId);
                     if (asc != null) {
-                        mSyncJobService.callJobFinished(syncOperation.jobId, false,
+                        SyncJobService.callJobFinished(syncOperation.jobId, false,
                                 "removePeriodicSyncInternalH");
                         runSyncFinishedOrCanceledH(null, asc);
                     }
@@ -3662,7 +3593,7 @@
                                     false /* no config settings */)) {
                         continue;
                     }
-                    mSyncJobService.callJobFinished(activeSyncContext.mSyncOperation.jobId, false,
+                    SyncJobService.callJobFinished(activeSyncContext.mSyncOperation.jobId, false,
                             why);
                     runSyncFinishedOrCanceledH(null /* cancel => no result */, activeSyncContext);
                 }
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 811dc75..391e3b0 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -19,6 +19,7 @@
 import android.accounts.Account;
 import android.accounts.AccountAndUser;
 import android.accounts.AccountManager;
+import android.annotation.Nullable;
 import android.app.backup.BackupManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -1005,7 +1006,7 @@
      * Called when the set of account has changed, given the new array of
      * active accounts.
      */
-    public void doDatabaseCleanup(Account[] accounts, int userId) {
+    public void removeStaleAccounts(@Nullable Account[] accounts, int userId) {
         synchronized (mAuthorities) {
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Slog.v(TAG, "Updating for new accounts...");
@@ -1014,8 +1015,9 @@
             Iterator<AccountInfo> accIt = mAccounts.values().iterator();
             while (accIt.hasNext()) {
                 AccountInfo acc = accIt.next();
-                if (!ArrayUtils.contains(accounts, acc.accountAndUser.account)
-                        && acc.accountAndUser.userId == userId) {
+                if ((accounts == null) || (
+                        (acc.accountAndUser.userId == userId)
+                        && !ArrayUtils.contains(accounts, acc.accountAndUser.account))) {
                     // This account no longer exists...
                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
                         Slog.v(TAG, "Account removed: " + acc.accountAndUser);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 5239fe5..35f64a1 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -974,7 +974,8 @@
             final long token = Binder.clearCallingIdentity();
             try {
                 final int packageUid = mPackageManagerInternal.getPackageUid(packageName,
-                        PackageManager.MATCH_ANY_USER, userId);
+                        PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                        | PackageManager.MATCH_DIRECT_BOOT_AWARE, userId);
                 // Caller cannot set their own standby state
                 if (packageUid == callingUid) {
                     throw new IllegalArgumentException("Cannot set your own standby bucket");