Skip to content

Commit 69d95de

Browse files
Synchronize/align periodic sync alarms based on a random per device seed value.
Removes duplicates SyncHandler.MESSAGE_CHECK_ALARM messages from the queue. Change-Id: Ib29a06da904cebdd45ee05c9d9a6bf00b72100eb
1 parent b874e5b commit 69d95de

File tree

2 files changed

+65
-14
lines changed

2 files changed

+65
-14
lines changed

core/java/android/content/SyncManager.java

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,9 @@ public void onReceive(Context context, Intent intent) {
204204

205205
private final PowerManager mPowerManager;
206206

207+
// Use this as a random offset to seed all periodic syncs
208+
private int mSyncRandomOffsetMillis;
209+
207210
private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds
208211
private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours
209212

@@ -472,6 +475,9 @@ public void onStatusChanged(int which) {
472475
// do this synchronously to ensure we have the accounts before this call returns
473476
onAccountsUpdated(null);
474477
}
478+
479+
// Pick a random second in a day to seed all periodic syncs
480+
mSyncRandomOffsetMillis = mSyncStorageEngine.getSyncRandomOffset() * 1000;
475481
}
476482

477483
/**
@@ -700,6 +706,7 @@ private void sendSyncAlarmMessage() {
700706

701707
private void sendCheckAlarmsMessage() {
702708
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CHECK_ALARMS");
709+
mSyncHandler.removeMessages(SyncHandler.MESSAGE_CHECK_ALARMS);
703710
mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_CHECK_ALARMS);
704711
}
705712

@@ -748,6 +755,8 @@ private void clearBackoffSetting(SyncOperation op) {
748755
}
749756

750757
private void increaseBackoffSetting(SyncOperation op) {
758+
// TODO: Use this function to align it to an already scheduled sync
759+
// operation in the specified window
751760
final long now = SystemClock.elapsedRealtime();
752761

753762
final Pair<Long, Long> previousSettings =
@@ -1094,6 +1103,8 @@ protected void dumpSyncState(PrintWriter pw) {
10941103
final long now = SystemClock.elapsedRealtime();
10951104
pw.print("now: "); pw.print(now);
10961105
pw.println(" (" + formatTime(System.currentTimeMillis()) + ")");
1106+
pw.print("offset: "); pw.print(DateUtils.formatElapsedTime(mSyncRandomOffsetMillis/1000));
1107+
pw.println(" (HH:MM:SS)");
10971108
pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now/1000));
10981109
pw.println(" (HH:MM:SS)");
10991110
pw.print("time spent syncing: ");
@@ -1805,6 +1816,9 @@ private long scheduleReadyPeriodicSyncs() {
18051816
AccountAndUser[] accounts = mAccounts;
18061817

18071818
final long nowAbsolute = System.currentTimeMillis();
1819+
final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis)
1820+
? (nowAbsolute - mSyncRandomOffsetMillis) : 0;
1821+
18081822
ArrayList<SyncStorageEngine.AuthorityInfo> infos = mSyncStorageEngine.getAuthorities();
18091823
for (SyncStorageEngine.AuthorityInfo info : infos) {
18101824
// skip the sync if the account of this operation no longer exists
@@ -1826,16 +1840,32 @@ private long scheduleReadyPeriodicSyncs() {
18261840
SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(info);
18271841
for (int i = 0, N = info.periodicSyncs.size(); i < N; i++) {
18281842
final Bundle extras = info.periodicSyncs.get(i).first;
1829-
final Long periodInSeconds = info.periodicSyncs.get(i).second;
1843+
final Long periodInMillis = info.periodicSyncs.get(i).second * 1000;
18301844
// find when this periodic sync was last scheduled to run
18311845
final long lastPollTimeAbsolute = status.getPeriodicSyncTime(i);
1832-
// compute when this periodic sync should next run - this can be in the future
1833-
// for example if the user changed the time, synced and changed back.
1834-
final long nextPollTimeAbsolute = lastPollTimeAbsolute > nowAbsolute
1835-
? nowAbsolute
1836-
: lastPollTimeAbsolute + periodInSeconds * 1000;
1837-
// if it is ready to run then schedule it and mark it as having been scheduled
1838-
if (nextPollTimeAbsolute <= nowAbsolute) {
1846+
1847+
long remainingMillis
1848+
= periodInMillis - (shiftedNowAbsolute % periodInMillis);
1849+
1850+
/*
1851+
* Sync scheduling strategy:
1852+
* Set the next periodic sync based on a random offset (in seconds).
1853+
*
1854+
* Also sync right now if any of the following cases hold
1855+
* and mark it as having been scheduled
1856+
*
1857+
* Case 1: This sync is ready to run now.
1858+
* Case 2: If the lastPollTimeAbsolute is in the future,
1859+
* sync now and reinitialize. This can happen for
1860+
* example if the user changed the time, synced and
1861+
* changed back.
1862+
* Case 3: If we failed to sync at the last scheduled time
1863+
*/
1864+
if (remainingMillis == periodInMillis // Case 1
1865+
|| lastPollTimeAbsolute > nowAbsolute // Case 2
1866+
|| (nowAbsolute - lastPollTimeAbsolute
1867+
>= periodInMillis)) { // Case 3
1868+
// Sync now
18391869
final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(
18401870
info.account, info.userId, info.authority);
18411871
final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
@@ -1853,12 +1883,13 @@ private long scheduleReadyPeriodicSyncs() {
18531883
info.account, info.userId, info.authority),
18541884
syncAdapterInfo.type.allowParallelSyncs()));
18551885
status.setPeriodicSyncTime(i, nowAbsolute);
1856-
} else {
1857-
// it isn't ready to run, remember this time if it is earlier than
1858-
// earliestFuturePollTime
1859-
if (nextPollTimeAbsolute < earliestFuturePollTime) {
1860-
earliestFuturePollTime = nextPollTimeAbsolute;
1861-
}
1886+
}
1887+
// Compute when this periodic sync should next run
1888+
final long nextPollTimeAbsolute = nowAbsolute + remainingMillis;
1889+
1890+
// remember this time if it is earlier than earliestFuturePollTime
1891+
if (nextPollTimeAbsolute < earliestFuturePollTime) {
1892+
earliestFuturePollTime = nextPollTimeAbsolute;
18621893
}
18631894
}
18641895
}

core/java/android/content/SyncStorageEngine.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import android.os.Parcel;
3838
import android.os.RemoteCallbackList;
3939
import android.os.RemoteException;
40+
import android.os.SystemClock;
4041
import android.util.Log;
4142
import android.util.SparseArray;
4243
import android.util.Xml;
@@ -49,6 +50,7 @@
4950
import java.util.Calendar;
5051
import java.util.HashMap;
5152
import java.util.Iterator;
53+
import java.util.Random;
5254
import java.util.TimeZone;
5355
import java.util.List;
5456

@@ -65,6 +67,7 @@ public class SyncStorageEngine extends Handler {
6567

6668
private static final String XML_ATTR_NEXT_AUTHORITY_ID = "nextAuthorityId";
6769
private static final String XML_ATTR_LISTEN_FOR_TICKLES = "listen-for-tickles";
70+
private static final String XML_ATTR_SYNC_RANDOM_OFFSET = "offsetInSeconds";
6871
private static final String XML_ATTR_ENABLED = "enabled";
6972
private static final String XML_ATTR_USER = "user";
7073
private static final String XML_TAG_LISTEN_FOR_TICKLES = "listenForTickles";
@@ -277,6 +280,8 @@ interface OnSyncRequestListener {
277280

278281
private static volatile SyncStorageEngine sSyncStorageEngine = null;
279282

283+
private int mSyncRandomOffset;
284+
280285
/**
281286
* This file contains the core engine state: all accounts and the
282287
* settings for them. It must never be lost, and should be changed
@@ -375,6 +380,10 @@ protected void setOnSyncRequestListener(OnSyncRequestListener listener) {
375380
}
376381
}
377382

383+
public int getSyncRandomOffset() {
384+
return mSyncRandomOffset;
385+
}
386+
378387
public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
379388
synchronized (mAuthorities) {
380389
mChangeListeners.register(callback, mask);
@@ -1465,6 +1474,16 @@ private void readAccountInfoLocked() {
14651474
} catch (NumberFormatException e) {
14661475
// don't care
14671476
}
1477+
String offsetString = parser.getAttributeValue(null, XML_ATTR_SYNC_RANDOM_OFFSET);
1478+
try {
1479+
mSyncRandomOffset = (offsetString == null) ? 0 : Integer.parseInt(offsetString);
1480+
} catch (NumberFormatException e) {
1481+
mSyncRandomOffset = 0;
1482+
}
1483+
if (mSyncRandomOffset == 0) {
1484+
Random random = new Random(System.currentTimeMillis());
1485+
mSyncRandomOffset = random.nextInt(86400);
1486+
}
14681487
mMasterSyncAutomatically.put(0, listen == null || Boolean.parseBoolean(listen));
14691488
eventType = parser.next();
14701489
AuthorityInfo authority = null;
@@ -1705,6 +1724,7 @@ private void writeAccountInfoLocked() {
17051724
out.startTag(null, "accounts");
17061725
out.attribute(null, "version", Integer.toString(ACCOUNTS_VERSION));
17071726
out.attribute(null, XML_ATTR_NEXT_AUTHORITY_ID, Integer.toString(mNextAuthorityId));
1727+
out.attribute(null, XML_ATTR_SYNC_RANDOM_OFFSET, Integer.toString(mSyncRandomOffset));
17081728

17091729
// Write the Sync Automatically flags for each user
17101730
final int M = mMasterSyncAutomatically.size();

0 commit comments

Comments
 (0)