From 8b76d20c71353fe98d898a863e7660c00a09dcb6 Mon Sep 17 00:00:00 2001 From: Matthew Williams Date: Sun, 3 May 2015 18:16:25 -0700 Subject: Delay all syncing until the device is provisioned BUG: 20185340 Add ContentObserver to be notified when device provisioned system setting bit is flipped. Stash all messages that come along before then, similar to how we were doing it for onBootCompleted, and fire them off when ready. Change-Id: Ied9fe2262b2a96ea3316b6dd7c57e23628e19581 --- .../com/android/server/content/SyncManager.java | 138 ++++++++++++++++----- 1 file changed, 104 insertions(+), 34 deletions(-) diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 7cccef2e8fcf..3dc282bb7d81 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -52,6 +52,7 @@ import android.content.pm.RegisteredServicesCache; import android.content.pm.RegisteredServicesCacheListener; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; +import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.BatteryStats; @@ -99,6 +100,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; @@ -157,7 +159,19 @@ public class SyncManager { /** * How long to wait before considering an active sync to have timed-out, and cancelling it. */ - private static final long ACTIVE_SYNC_TIMEOUT_MILLIS = 30L * 60 * 1000; // 30 mins. + private static final long ACTIVE_SYNC_TIMEOUT_MILLIS = 30L * 60 * 1000; // 30 mins + + /** + * How long to delay each queued {@link SyncHandler} message that may have occurred before boot + * or befor the device became provisioned. + */ + private static final long PER_SYNC_BOOT_DELAY_MILLIS = 3000L; // 3 seconds + + /** + * The maximum amount of time we're willing to delay syncs out of boot, after device has been + * provisioned, etc. + */ + private static final long MAX_SYNC_BOOT_DELAY_MILLIS = 120000L; // 2 minutes private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*/"; private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm"; @@ -198,6 +212,9 @@ public class SyncManager { // its accessor, getConnManager(). private ConnectivityManager mConnManagerDoNotUseDirectly; + /** Track whether the device has already been provisioned. */ + private boolean mProvisioned; + protected SyncAdaptersCache mSyncAdapters; private final AppIdleMonitor mAppIdleMonitor; @@ -242,6 +259,7 @@ public class SyncManager { private BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + mBootCompleted = true; mSyncHandler.onBootCompleted(); } }; @@ -491,12 +509,41 @@ public class SyncManager { mSyncStorageEngine.addStatusChangeListener( ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, new ISyncStatusObserver.Stub() { - @Override - public void onStatusChanged(int which) { - // force the sync loop to run if the settings change - sendCheckAlarmsMessage(); + @Override + public void onStatusChanged(int which) { + // force the sync loop to run if the settings change + sendCheckAlarmsMessage(); + } + }); + + mProvisioned = isDeviceProvisioned(); + if (!mProvisioned) { + final ContentResolver resolver = context.getContentResolver(); + ContentObserver provisionedObserver = + new ContentObserver(null /* current thread */) { + public void onChange(boolean selfChange) { + mProvisioned |= isDeviceProvisioned(); + if (mProvisioned) { + mSyncHandler.onDeviceProvisioned(); + resolver.unregisterContentObserver(this); + } + } + }; + + synchronized (mSyncHandler) { + resolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), + false /* notifyForDescendents */, + provisionedObserver); + + // The device *may* have been provisioned while we were registering above observer. + // Check again to make sure. + mProvisioned |= isDeviceProvisioned(); + if (mProvisioned) { + resolver.unregisterContentObserver(provisionedObserver); + } } - }); + } if (!factoryTest) { // Register for account list updates for all users @@ -510,6 +557,10 @@ public class SyncManager { mSyncRandomOffsetMillis = mSyncStorageEngine.getSyncRandomOffset() * 1000; } + private boolean isDeviceProvisioned() { + final ContentResolver resolver = mContext.getContentResolver(); + return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0); + } /** * Return a random value v that satisfies minValue <= v < maxValue. The difference between * maxValue and minValue must be less than Integer.MAX_VALUE. @@ -2000,20 +2051,36 @@ public class SyncManager { public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker(); private final HashMap mWakeLocks = Maps.newHashMap(); - private List mBootQueue = new ArrayList(); + private List mUnreadyQueue = new ArrayList(); - public void onBootCompleted() { + void onBootCompleted() { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Boot completed, clearing boot queue."); } doDatabaseCleanup(); synchronized(this) { // Dispatch any stashed messages. - for (Message message : mBootQueue) { - sendMessage(message); + maybeEmptyUnreadyQueueLocked(); + } + } + + void onDeviceProvisioned() { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "mProvisioned=" + mProvisioned); + } + synchronized (this) { + maybeEmptyUnreadyQueueLocked(); + } + } + + private void maybeEmptyUnreadyQueueLocked() { + if (mProvisioned && mBootCompleted) { + // Dispatch any stashed messages. + for (int i=0; i