Skip to content

Commit 9766937

Browse files
ashish-s-sharmaAndroid (Google) Code Review
authored andcommitted
Merge "Synchronize/align periodic sync alarms based on a random per device seed value."
2 parents f1352d8 + 69d95de commit 9766937

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
@@ -205,6 +205,9 @@ public void onReceive(Context context, Intent intent) {
205205

206206
private final PowerManager mPowerManager;
207207

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

@@ -438,6 +441,9 @@ public void onStatusChanged(int which) {
438441
// do this synchronously to ensure we have the accounts before this call returns
439442
onAccountsUpdated(null);
440443
}
444+
445+
// Pick a random second in a day to seed all periodic syncs
446+
mSyncRandomOffsetMillis = mSyncStorageEngine.getSyncRandomOffset() * 1000;
441447
}
442448

443449
/**
@@ -666,6 +672,7 @@ private void sendSyncAlarmMessage() {
666672

667673
private void sendCheckAlarmsMessage() {
668674
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CHECK_ALARMS");
675+
mSyncHandler.removeMessages(SyncHandler.MESSAGE_CHECK_ALARMS);
669676
mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_CHECK_ALARMS);
670677
}
671678

@@ -714,6 +721,8 @@ private void clearBackoffSetting(SyncOperation op) {
714721
}
715722

716723
private void increaseBackoffSetting(SyncOperation op) {
724+
// TODO: Use this function to align it to an already scheduled sync
725+
// operation in the specified window
717726
final long now = SystemClock.elapsedRealtime();
718727

719728
final Pair<Long, Long> previousSettings =
@@ -1060,6 +1069,8 @@ protected void dumpSyncState(PrintWriter pw) {
10601069
final long now = SystemClock.elapsedRealtime();
10611070
pw.print("now: "); pw.print(now);
10621071
pw.println(" (" + formatTime(System.currentTimeMillis()) + ")");
1072+
pw.print("offset: "); pw.print(DateUtils.formatElapsedTime(mSyncRandomOffsetMillis/1000));
1073+
pw.println(" (HH:MM:SS)");
10631074
pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now/1000));
10641075
pw.println(" (HH:MM:SS)");
10651076
pw.print("time spent syncing: ");
@@ -1771,6 +1782,9 @@ private long scheduleReadyPeriodicSyncs() {
17711782
AccountAndUser[] accounts = mAccounts;
17721783

17731784
final long nowAbsolute = System.currentTimeMillis();
1785+
final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis)
1786+
? (nowAbsolute - mSyncRandomOffsetMillis) : 0;
1787+
17741788
ArrayList<SyncStorageEngine.AuthorityInfo> infos = mSyncStorageEngine.getAuthorities();
17751789
for (SyncStorageEngine.AuthorityInfo info : infos) {
17761790
// skip the sync if the account of this operation no longer exists
@@ -1792,16 +1806,32 @@ private long scheduleReadyPeriodicSyncs() {
17921806
SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(info);
17931807
for (int i = 0, N = info.periodicSyncs.size(); i < N; i++) {
17941808
final Bundle extras = info.periodicSyncs.get(i).first;
1795-
final Long periodInSeconds = info.periodicSyncs.get(i).second;
1809+
final Long periodInMillis = info.periodicSyncs.get(i).second * 1000;
17961810
// find when this periodic sync was last scheduled to run
17971811
final long lastPollTimeAbsolute = status.getPeriodicSyncTime(i);
1798-
// compute when this periodic sync should next run - this can be in the future
1799-
// for example if the user changed the time, synced and changed back.
1800-
final long nextPollTimeAbsolute = lastPollTimeAbsolute > nowAbsolute
1801-
? nowAbsolute
1802-
: lastPollTimeAbsolute + periodInSeconds * 1000;
1803-
// if it is ready to run then schedule it and mark it as having been scheduled
1804-
if (nextPollTimeAbsolute <= nowAbsolute) {
1812+
1813+
long remainingMillis
1814+
= periodInMillis - (shiftedNowAbsolute % periodInMillis);
1815+
1816+
/*
1817+
* Sync scheduling strategy:
1818+
* Set the next periodic sync based on a random offset (in seconds).
1819+
*
1820+
* Also sync right now if any of the following cases hold
1821+
* and mark it as having been scheduled
1822+
*
1823+
* Case 1: This sync is ready to run now.
1824+
* Case 2: If the lastPollTimeAbsolute is in the future,
1825+
* sync now and reinitialize. This can happen for
1826+
* example if the user changed the time, synced and
1827+
* changed back.
1828+
* Case 3: If we failed to sync at the last scheduled time
1829+
*/
1830+
if (remainingMillis == periodInMillis // Case 1
1831+
|| lastPollTimeAbsolute > nowAbsolute // Case 2
1832+
|| (nowAbsolute - lastPollTimeAbsolute
1833+
>= periodInMillis)) { // Case 3
1834+
// Sync now
18051835
final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(
18061836
info.account, info.userId, info.authority);
18071837
final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
@@ -1819,12 +1849,13 @@ private long scheduleReadyPeriodicSyncs() {
18191849
info.account, info.userId, info.authority),
18201850
syncAdapterInfo.type.allowParallelSyncs()));
18211851
status.setPeriodicSyncTime(i, nowAbsolute);
1822-
} else {
1823-
// it isn't ready to run, remember this time if it is earlier than
1824-
// earliestFuturePollTime
1825-
if (nextPollTimeAbsolute < earliestFuturePollTime) {
1826-
earliestFuturePollTime = nextPollTimeAbsolute;
1827-
}
1852+
}
1853+
// Compute when this periodic sync should next run
1854+
final long nextPollTimeAbsolute = nowAbsolute + remainingMillis;
1855+
1856+
// remember this time if it is earlier than earliestFuturePollTime
1857+
if (nextPollTimeAbsolute < earliestFuturePollTime) {
1858+
earliestFuturePollTime = nextPollTimeAbsolute;
18281859
}
18291860
}
18301861
}

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)