Logging and readability improvements

Rearrange the refresh logic:
1) One log entry per refresh rather than (sometimes) 2.
2) Rearrange the logic to try to keep separate concerns separate.
3) Prepare for the next commit in the chain that modifies scheduling
   behavior.

Bug: 222295093
Test: atest services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java
Change-Id: I72976e9c7d0e0193c3f3f5e93bfda128defa3145
This commit is contained in:
Neil Fuller
2022-12-14 15:52:49 +00:00
parent d7be628d05
commit 469925d2aa
2 changed files with 309 additions and 87 deletions

View File

@@ -358,9 +358,30 @@ public class NetworkTimeUpdateService extends Binder {
@NonNull
private final LocalLog mLocalDebugLog = new LocalLog(30, false /* useLocalTimestamps */);
/**
* The usual interval between refresh attempts. Always used after a successful request.
*
* <p>The value also determines whether a network time result is considered fresh.
* Refreshes only take place from this class when the latest time result is considered too
* old.
*/
private final int mNormalPollingIntervalMillis;
/**
* A shortened interval between refresh attempts used after a failure to refresh.
* Always shorter than {@link #mNormalPollingIntervalMillis} and only used when {@link
* #mTryAgainTimesMax} != 0.
*/
private final int mShortPollingIntervalMillis;
/**
* The number of times {@link #mShortPollingIntervalMillis} can be used after successive
* failures before switching back to using {@link #mNormalPollingIntervalMillis} once before
* repeating. When this value is negative, the refresh algorithm will continue to use {@link
* #mShortPollingIntervalMillis} until a successful refresh.
*/
private final int mTryAgainTimesMax;
private final NtpTrustedTime mNtpTrustedTime;
/**
@@ -378,6 +399,11 @@ public class NetworkTimeUpdateService extends Binder {
int normalPollingIntervalMillis, int shortPollingIntervalMillis,
int tryAgainTimesMax, @NonNull NtpTrustedTime ntpTrustedTime) {
mElapsedRealtimeMillisSupplier = Objects.requireNonNull(elapsedRealtimeMillisSupplier);
if (shortPollingIntervalMillis > normalPollingIntervalMillis) {
throw new IllegalArgumentException(String.format(
"shortPollingIntervalMillis (%s) > normalPollingIntervalMillis (%s)",
shortPollingIntervalMillis, normalPollingIntervalMillis));
}
mNormalPollingIntervalMillis = normalPollingIntervalMillis;
mShortPollingIntervalMillis = shortPollingIntervalMillis;
mTryAgainTimesMax = tryAgainTimesMax;
@@ -387,81 +413,121 @@ public class NetworkTimeUpdateService extends Binder {
@Override
public boolean forceRefreshForTests(
@NonNull Network network, @NonNull RefreshCallbacks refreshCallbacks) {
boolean success = mNtpTrustedTime.forceRefresh(network);
logToDebugAndDumpsys("forceRefreshForTests: success=" + success);
boolean refreshSuccessful = mNtpTrustedTime.forceRefresh(network);
logToDebugAndDumpsys("forceRefreshForTests: refreshSuccessful=" + refreshSuccessful);
if (success) {
if (refreshSuccessful) {
makeNetworkTimeSuggestion(mNtpTrustedTime.getCachedTimeResult(),
"EngineImpl.forceRefreshForTests()", refreshCallbacks);
}
return success;
return refreshSuccessful;
}
@Override
public void refreshIfRequiredAndReschedule(
@NonNull Network network, @NonNull String reason,
@NonNull RefreshCallbacks refreshCallbacks) {
long currentElapsedRealtimeMillis = mElapsedRealtimeMillisSupplier.get();
final int maxNetworkTimeAgeMillis = mNormalPollingIntervalMillis;
// Force an NTP fix when outdated
// Attempt to refresh the network time if there is no latest time result, or if the
// latest time result is considered too old.
NtpTrustedTime.TimeResult initialTimeResult = mNtpTrustedTime.getCachedTimeResult();
if (calculateTimeResultAgeMillis(initialTimeResult, currentElapsedRealtimeMillis)
>= maxNetworkTimeAgeMillis) {
if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh using network=" + network);
boolean successful = mNtpTrustedTime.forceRefresh(network);
if (successful) {
synchronized (this) {
mTryAgainCounter = 0;
}
} else {
String logMsg = "forceRefresh() returned false:"
+ " initialTimeResult=" + initialTimeResult
+ ", currentElapsedRealtimeMillis=" + currentElapsedRealtimeMillis;
logToDebugAndDumpsys(logMsg);
}
boolean shouldAttemptRefresh;
synchronized (this) {
long currentElapsedRealtimeMillis = mElapsedRealtimeMillisSupplier.get();
// calculateTimeResultAgeMillis() safely handles a null initialTimeResult.
long timeResultAgeMillis = calculateTimeResultAgeMillis(
initialTimeResult, currentElapsedRealtimeMillis);
shouldAttemptRefresh = timeResultAgeMillis >= mNormalPollingIntervalMillis;
}
boolean refreshSuccessful = false;
if (shouldAttemptRefresh) {
// This is a blocking call. Deliberately invoked without holding the "this" monitor
// to avoid blocking logic that wants to use the "this" monitor.
refreshSuccessful = mNtpTrustedTime.forceRefresh(network);
}
synchronized (this) {
long nextPollDelayMillis;
NtpTrustedTime.TimeResult latestTimeResult = mNtpTrustedTime.getCachedTimeResult();
if (calculateTimeResultAgeMillis(latestTimeResult, currentElapsedRealtimeMillis)
< maxNetworkTimeAgeMillis) {
// Obtained fresh fix; schedule next normal update
nextPollDelayMillis = mNormalPollingIntervalMillis
- latestTimeResult.getAgeMillis(currentElapsedRealtimeMillis);
makeNetworkTimeSuggestion(latestTimeResult, reason, refreshCallbacks);
} else {
// No fresh fix; schedule retry
mTryAgainCounter++;
if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
nextPollDelayMillis = mShortPollingIntervalMillis;
} else {
// Try much later
// Manage mTryAgainCounter.
if (shouldAttemptRefresh) {
if (refreshSuccessful) {
// Reset failure tracking.
mTryAgainCounter = 0;
nextPollDelayMillis = mNormalPollingIntervalMillis;
} else {
if (mTryAgainTimesMax < 0) {
// When mTryAgainTimesMax is negative there's no enforced maximum and
// short intervals should be used until a successful refresh. Setting
// mTryAgainCounter to 1 is sufficient for the interval calculations
// below. There's no need to increment.
mTryAgainCounter = 1;
} else {
mTryAgainCounter++;
if (mTryAgainCounter > mTryAgainTimesMax) {
mTryAgainCounter = 0;
}
}
}
}
// currentElapsedRealtimeMillis is used to evaluate ages and refresh scheduling
// below. Capturing this after a possible successful refresh ensures that latest
// time result ages will be >= 0.
long currentElapsedRealtimeMillis = mElapsedRealtimeMillisSupplier.get();
// This section of code deliberately doesn't assume it is the only component using
// mNtpTrustedTime to obtain NTP times: another component in the same process could
// be gathering NTP signals (which then won't have been suggested to the time
// detector).
// TODO(b/222295093): Make this class the sole owner of mNtpTrustedTime and
// simplify / reduce duplicate suggestions.
NtpTrustedTime.TimeResult latestTimeResult = mNtpTrustedTime.getCachedTimeResult();
long latestTimeResultAgeMillis = calculateTimeResultAgeMillis(
latestTimeResult, currentElapsedRealtimeMillis);
// Suggest the latest time result to the time detector if it is fresh regardless of
// whether refresh happened above.
if (latestTimeResultAgeMillis < mNormalPollingIntervalMillis) {
// We assume the time detector service will detect duplicate suggestions and not
// do more work than it has to, so no need to avoid making duplicate
// suggestions.
makeNetworkTimeSuggestion(latestTimeResult, reason, refreshCallbacks);
}
// (Re)schedule the next refresh based on the latest state.
// Determine which refresh delay to use by using the current value of
// mTryAgainCounter.
long refreshDelayMillis = mTryAgainCounter > 0
? mShortPollingIntervalMillis : mNormalPollingIntervalMillis;
// Adjust the next refresh time for the age of the latest time result.
if (latestTimeResultAgeMillis < refreshDelayMillis) {
refreshDelayMillis -= latestTimeResultAgeMillis;
}
long nextRefreshElapsedRealtimeMillis =
currentElapsedRealtimeMillis + nextPollDelayMillis;
currentElapsedRealtimeMillis + refreshDelayMillis;
refreshCallbacks.scheduleNextRefresh(nextRefreshElapsedRealtimeMillis);
logToDebugAndDumpsys("refreshIfRequiredAndReschedule:"
+ " network=" + network
+ ", reason=" + reason
+ ", currentElapsedRealtimeMillis=" + currentElapsedRealtimeMillis
+ ", initialTimeResult=" + initialTimeResult
+ ", shouldAttemptRefresh=" + shouldAttemptRefresh
+ ", refreshSuccessful=" + refreshSuccessful
+ ", currentElapsedRealtimeMillis="
+ formatElapsedRealtimeMillis(currentElapsedRealtimeMillis)
+ ", latestTimeResult=" + latestTimeResult
+ ", mTryAgainCounter=" + mTryAgainCounter
+ ", nextPollDelayMillis=" + nextPollDelayMillis
+ ", refreshDelayMillis=" + refreshDelayMillis
+ ", nextRefreshElapsedRealtimeMillis="
+ Duration.ofMillis(nextRefreshElapsedRealtimeMillis)
+ " (" + nextRefreshElapsedRealtimeMillis + ")");
+ formatElapsedRealtimeMillis(nextRefreshElapsedRealtimeMillis));
}
}
private static String formatElapsedRealtimeMillis(
@ElapsedRealtimeLong long elapsedRealtimeMillis) {
return Duration.ofMillis(elapsedRealtimeMillis) + " (" + elapsedRealtimeMillis + ")";
}
private static long calculateTimeResultAgeMillis(
@Nullable TimeResult timeResult,
@ElapsedRealtimeLong long currentElapsedRealtimeMillis) {

View File

@@ -74,10 +74,13 @@ public class NetworkTimeUpdateServiceTest {
// Simulated NTP client behavior: No cached time value available initially, then a
// successful refresh.
NtpTrustedTime.TimeResult timeResult = createNtpTimeResult(
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis() - 1);
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis());
when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null, timeResult);
when(mMockNtpTrustedTime.forceRefresh(mDummyNetwork)).thenReturn(true);
// Simulate the passage of time for realism.
mFakeElapsedRealtimeClock.incrementMillis(5000);
RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
// Trigger the engine's logic.
engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
@@ -86,10 +89,9 @@ public class NetworkTimeUpdateServiceTest {
verify(mMockNtpTrustedTime).forceRefresh(mDummyNetwork);
// Check everything happened that was supposed to.
long expectedDelayMillis = calculateRefreshDelayMillisForTimeResult(
timeResult, normalPollingIntervalMillis);
long expectedDelayMillis = normalPollingIntervalMillis;
verify(mockCallback).scheduleNextRefresh(
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis() + expectedDelayMillis);
timeResult.getElapsedRealtimeMillis() + expectedDelayMillis);
NetworkTimeSuggestion expectedSuggestion = createExpectedSuggestion(timeResult);
verify(mockCallback).submitSuggestion(expectedSuggestion);
@@ -108,6 +110,9 @@ public class NetworkTimeUpdateServiceTest {
mMockNtpTrustedTime);
for (int i = 0; i < tryAgainTimesMax + 1; i++) {
// Simulate the passage of time for realism.
mFakeElapsedRealtimeClock.incrementMillis(5000);
// Simulated NTP client behavior: No cached time value available and failure to refresh.
when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null);
when(mMockNtpTrustedTime.forceRefresh(mDummyNetwork)).thenReturn(false);
@@ -140,7 +145,6 @@ public class NetworkTimeUpdateServiceTest {
mFakeElapsedRealtimeClock.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
int normalPollingIntervalMillis = 7777777;
int maxTimeResultAgeMillis = normalPollingIntervalMillis;
int shortPollingIntervalMillis = 3333;
int tryAgainTimesMax = 5;
NetworkTimeUpdateService.Engine engine = new NetworkTimeUpdateService.EngineImpl(
@@ -149,7 +153,7 @@ public class NetworkTimeUpdateServiceTest {
mMockNtpTrustedTime);
NtpTrustedTime.TimeResult timeResult = createNtpTimeResult(
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis() - 1);
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis());
NetworkTimeSuggestion expectedSuggestion = createExpectedSuggestion(timeResult);
{
@@ -158,6 +162,9 @@ public class NetworkTimeUpdateServiceTest {
when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null, timeResult);
when(mMockNtpTrustedTime.forceRefresh(mDummyNetwork)).thenReturn(true);
// Simulate the passage of time for realism.
mFakeElapsedRealtimeClock.incrementMillis(5000);
RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
// Trigger the engine's logic.
@@ -167,17 +174,16 @@ public class NetworkTimeUpdateServiceTest {
// initially.
verify(mMockNtpTrustedTime).forceRefresh(mDummyNetwork);
long expectedDelayMillis = calculateRefreshDelayMillisForTimeResult(
timeResult, normalPollingIntervalMillis);
long expectedDelayMillis = normalPollingIntervalMillis;
verify(mockCallback).scheduleNextRefresh(
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis() + expectedDelayMillis);
timeResult.getElapsedRealtimeMillis() + expectedDelayMillis);
verify(mockCallback, times(1)).submitSuggestion(expectedSuggestion);
reset(mMockNtpTrustedTime);
}
// Increment the current time by enough so that an attempt to refresh the time should be
// made every time refreshIfRequiredAndReschedule() is called.
mFakeElapsedRealtimeClock.incrementMillis(maxTimeResultAgeMillis);
mFakeElapsedRealtimeClock.incrementMillis(normalPollingIntervalMillis);
// Test multiple follow-up calls.
for (int i = 0; i < tryAgainTimesMax + 1; i++) {
@@ -208,30 +214,37 @@ public class NetworkTimeUpdateServiceTest {
verify(mockCallback, never()).submitSuggestion(any());
reset(mMockNtpTrustedTime);
// Simulate the passage of time for realism.
mFakeElapsedRealtimeClock.incrementMillis(5000);
}
}
@Test
public void engineImpl_refreshIfRequiredAndReschedule_successFailSuccess() {
public void engineImpl_refreshIfRequiredAndReschedule_successThenFail_tryAgainTimesZero() {
mFakeElapsedRealtimeClock.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
int normalPollingIntervalMillis = 7777777;
int maxTimeResultAgeMillis = normalPollingIntervalMillis;
int shortPollingIntervalMillis = 3333;
int tryAgainTimesMax = 5;
int tryAgainTimesMax = 0;
NetworkTimeUpdateService.Engine engine = new NetworkTimeUpdateService.EngineImpl(
mFakeElapsedRealtimeClock,
normalPollingIntervalMillis, shortPollingIntervalMillis, tryAgainTimesMax,
mMockNtpTrustedTime);
NtpTrustedTime.TimeResult timeResult1 = createNtpTimeResult(
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis() - 1);
NtpTrustedTime.TimeResult timeResult = createNtpTimeResult(
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis());
NetworkTimeSuggestion expectedSuggestion = createExpectedSuggestion(timeResult);
{
// Simulated NTP client behavior: No cached time value available initially, with a
// successful refresh.
when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null, timeResult1);
when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null, timeResult);
when(mMockNtpTrustedTime.forceRefresh(mDummyNetwork)).thenReturn(true);
// Simulate the passage of time for realism.
mFakeElapsedRealtimeClock.incrementMillis(5000);
RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
// Trigger the engine's logic.
@@ -241,10 +254,159 @@ public class NetworkTimeUpdateServiceTest {
// initially.
verify(mMockNtpTrustedTime).forceRefresh(mDummyNetwork);
long expectedDelayMillis = calculateRefreshDelayMillisForTimeResult(
timeResult1, normalPollingIntervalMillis);
long expectedDelayMillis = normalPollingIntervalMillis;
verify(mockCallback).scheduleNextRefresh(
timeResult.getElapsedRealtimeMillis() + expectedDelayMillis);
verify(mockCallback, times(1)).submitSuggestion(expectedSuggestion);
reset(mMockNtpTrustedTime);
}
// Increment the current time by enough so that an attempt to refresh the time should be
// made every time refreshIfRequiredAndReschedule() is called.
mFakeElapsedRealtimeClock.incrementMillis(normalPollingIntervalMillis);
// Test multiple follow-up calls.
for (int i = 0; i < 3; i++) {
// Simulated NTP client behavior: (Too old) cached time value available, unsuccessful
// refresh.
when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(timeResult);
when(mMockNtpTrustedTime.forceRefresh(mDummyNetwork)).thenReturn(false);
RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
// Trigger the engine's logic.
engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
// Expect a refresh attempt each time as the cached network time is too old.
verify(mMockNtpTrustedTime).forceRefresh(mDummyNetwork);
// Check the scheduling. tryAgainTimesMax == 0, so the algorithm should start with
long expectedDelayMillis = normalPollingIntervalMillis;
verify(mockCallback).scheduleNextRefresh(
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis() + expectedDelayMillis);
// No valid time, no suggestion.
verify(mockCallback, never()).submitSuggestion(any());
reset(mMockNtpTrustedTime);
// Simulate the passage of time for realism.
mFakeElapsedRealtimeClock.incrementMillis(5000);
}
}
@Test
public void engineImpl_refreshIfRequiredAndReschedule_successThenFail_tryAgainTimesNegative() {
mFakeElapsedRealtimeClock.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
int normalPollingIntervalMillis = 7777777;
int shortPollingIntervalMillis = 3333;
int tryAgainTimesMax = -1;
NetworkTimeUpdateService.Engine engine = new NetworkTimeUpdateService.EngineImpl(
mFakeElapsedRealtimeClock,
normalPollingIntervalMillis, shortPollingIntervalMillis, tryAgainTimesMax,
mMockNtpTrustedTime);
NtpTrustedTime.TimeResult timeResult = createNtpTimeResult(
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis());
NetworkTimeSuggestion expectedSuggestion = createExpectedSuggestion(timeResult);
{
// Simulated NTP client behavior: No cached time value available initially, with a
// successful refresh.
when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null, timeResult);
when(mMockNtpTrustedTime.forceRefresh(mDummyNetwork)).thenReturn(true);
// Simulate the passage of time for realism.
mFakeElapsedRealtimeClock.incrementMillis(5000);
RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
// Trigger the engine's logic.
engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
// Expect the refresh attempt to have been made: there is no cached network time
// initially.
verify(mMockNtpTrustedTime).forceRefresh(mDummyNetwork);
long expectedDelayMillis = normalPollingIntervalMillis;
verify(mockCallback).scheduleNextRefresh(
timeResult.getElapsedRealtimeMillis() + expectedDelayMillis);
verify(mockCallback, times(1)).submitSuggestion(expectedSuggestion);
reset(mMockNtpTrustedTime);
}
// Increment the current time by enough so that an attempt to refresh the time should be
// made every time refreshIfRequiredAndReschedule() is called.
mFakeElapsedRealtimeClock.incrementMillis(normalPollingIntervalMillis);
// Test multiple follow-up calls.
for (int i = 0; i < 3; i++) {
// Simulated NTP client behavior: (Too old) cached time value available, unsuccessful
// refresh.
when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(timeResult);
when(mMockNtpTrustedTime.forceRefresh(mDummyNetwork)).thenReturn(false);
RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
// Trigger the engine's logic.
engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
// Expect a refresh attempt each time as the cached network time is too old.
verify(mMockNtpTrustedTime).forceRefresh(mDummyNetwork);
// Check the scheduling. tryAgainTimesMax == -1, so it should always be
// shortPollingIntervalMillis.
long expectedDelayMillis = shortPollingIntervalMillis;
verify(mockCallback).scheduleNextRefresh(
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis() + expectedDelayMillis);
// No valid time, no suggestion.
verify(mockCallback, never()).submitSuggestion(any());
reset(mMockNtpTrustedTime);
// Simulate the passage of time for realism.
mFakeElapsedRealtimeClock.incrementMillis(5000);
}
}
@Test
public void engineImpl_refreshIfRequiredAndReschedule_successFailSuccess() {
mFakeElapsedRealtimeClock.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
int normalPollingIntervalMillis = 7777777;
int shortPollingIntervalMillis = 3333;
int tryAgainTimesMax = 5;
NetworkTimeUpdateService.Engine engine = new NetworkTimeUpdateService.EngineImpl(
mFakeElapsedRealtimeClock,
normalPollingIntervalMillis, shortPollingIntervalMillis, tryAgainTimesMax,
mMockNtpTrustedTime);
NtpTrustedTime.TimeResult timeResult1 = createNtpTimeResult(
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis());
{
// Simulated NTP client behavior: No cached time value available initially, with a
// successful refresh.
when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null, timeResult1);
when(mMockNtpTrustedTime.forceRefresh(mDummyNetwork)).thenReturn(true);
// Simulate the passage of time for realism.
mFakeElapsedRealtimeClock.incrementMillis(5000);
RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
// Trigger the engine's logic.
engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
// Expect the refresh attempt to have been made: there is no cached network time
// initially.
verify(mMockNtpTrustedTime).forceRefresh(mDummyNetwork);
long expectedDelayMillis = normalPollingIntervalMillis;
verify(mockCallback).scheduleNextRefresh(
timeResult1.getElapsedRealtimeMillis() + expectedDelayMillis);
NetworkTimeSuggestion expectedSuggestion = createExpectedSuggestion(timeResult1);
verify(mockCallback, times(1)).submitSuggestion(expectedSuggestion);
reset(mMockNtpTrustedTime);
@@ -253,7 +415,7 @@ public class NetworkTimeUpdateServiceTest {
// Increment the current time by enough so that the cached time result is too old and an
// attempt to refresh the time should be made every time refreshIfRequiredAndReschedule() is
// called.
mFakeElapsedRealtimeClock.incrementMillis(maxTimeResultAgeMillis);
mFakeElapsedRealtimeClock.incrementMillis(normalPollingIntervalMillis);
{
// Simulated NTP client behavior: (Old) cached time value available initially, with an
@@ -278,8 +440,11 @@ public class NetworkTimeUpdateServiceTest {
reset(mMockNtpTrustedTime);
}
// Increment time enough to avoid the minimum refresh interval protection.
mFakeElapsedRealtimeClock.incrementMillis(shortPollingIntervalMillis);
NtpTrustedTime.TimeResult timeResult2 = createNtpTimeResult(
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis() - 1);
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis());
{
// Simulated NTP client behavior: (Old) cached time value available initially, with a
@@ -287,6 +452,9 @@ public class NetworkTimeUpdateServiceTest {
when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(timeResult1, timeResult2);
when(mMockNtpTrustedTime.forceRefresh(mDummyNetwork)).thenReturn(true);
// Simulate the passage of time for realism.
mFakeElapsedRealtimeClock.incrementMillis(5000);
RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
// Trigger the engine's logic.
@@ -295,10 +463,9 @@ public class NetworkTimeUpdateServiceTest {
// Expect the refresh attempt to have been made: the timeResult is too old.
verify(mMockNtpTrustedTime).forceRefresh(mDummyNetwork);
long expectedDelayMillis = calculateRefreshDelayMillisForTimeResult(
timeResult2, normalPollingIntervalMillis);
long expectedDelayMillis = normalPollingIntervalMillis;
verify(mockCallback).scheduleNextRefresh(
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis() + expectedDelayMillis);
timeResult2.getElapsedRealtimeMillis() + expectedDelayMillis);
NetworkTimeSuggestion expectedSuggestion = createExpectedSuggestion(timeResult2);
verify(mockCallback, times(1)).submitSuggestion(expectedSuggestion);
reset(mMockNtpTrustedTime);
@@ -311,11 +478,10 @@ public class NetworkTimeUpdateServiceTest {
* A suggestion will still be made.
*/
@Test
public void engineImpl_refreshIfRequiredAndReschedule_noRefreshIfLatestIsNotTooOld() {
public void engineImpl_refreshIfRequiredAndReschedule_noRefreshIfLatestIsFresh() {
mFakeElapsedRealtimeClock.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
int normalPollingIntervalMillis = 7777777;
int maxTimeResultAgeMillis = normalPollingIntervalMillis;
int shortPollingIntervalMillis = 3333;
int tryAgainTimesMax = 5;
NetworkTimeUpdateService.Engine engine = new NetworkTimeUpdateService.EngineImpl(
@@ -323,12 +489,12 @@ public class NetworkTimeUpdateServiceTest {
normalPollingIntervalMillis, shortPollingIntervalMillis, tryAgainTimesMax,
mMockNtpTrustedTime);
// Simulated NTP client behavior: A cached time value is available, increment the clock, but
// not enough to consider the cached value too old.
// Simulated NTP client behavior: A cached time value is available.
NtpTrustedTime.TimeResult timeResult = createNtpTimeResult(
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis());
when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(timeResult);
mFakeElapsedRealtimeClock.incrementMillis(maxTimeResultAgeMillis - 1);
// Increment the clock, but not enough to consider the cached value too old.
mFakeElapsedRealtimeClock.incrementMillis(normalPollingIntervalMillis - 1);
RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
// Trigger the engine's logic.
@@ -339,10 +505,9 @@ public class NetworkTimeUpdateServiceTest {
// The next wake-up should be rescheduled for when the cached time value will become too
// old.
long expectedDelayMillis = calculateRefreshDelayMillisForTimeResult(timeResult,
normalPollingIntervalMillis);
long expectedDelayMillis = normalPollingIntervalMillis;
verify(mockCallback).scheduleNextRefresh(
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis() + expectedDelayMillis);
timeResult.getElapsedRealtimeMillis() + expectedDelayMillis);
// Suggestions must be made every time if the cached time value is not too old in case it
// was refreshed by a different component.
@@ -352,15 +517,13 @@ public class NetworkTimeUpdateServiceTest {
/**
* Confirms that if a refreshIfRequiredAndReschedule() call is made, e.g. for reasons besides
* scheduled alerts, and the latest time is not too old, then an NTP refresh won't be attempted.
* A suggestion will still be made.
* scheduled alerts, and the latest time is too old, then an NTP refresh will be attempted.
*/
@Test
public void engineImpl_refreshIfRequiredAndReschedule_failureHandlingAfterLatestIsTooOld() {
mFakeElapsedRealtimeClock.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
int normalPollingIntervalMillis = 7777777;
int maxTimeResultAgeMillis = normalPollingIntervalMillis;
int shortPollingIntervalMillis = 3333;
int tryAgainTimesMax = 5;
NetworkTimeUpdateService.Engine engine = new NetworkTimeUpdateService.EngineImpl(
@@ -373,7 +536,7 @@ public class NetworkTimeUpdateServiceTest {
NtpTrustedTime.TimeResult timeResult = createNtpTimeResult(
mFakeElapsedRealtimeClock.getElapsedRealtimeMillis());
when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(timeResult);
mFakeElapsedRealtimeClock.incrementMillis(maxTimeResultAgeMillis);
mFakeElapsedRealtimeClock.incrementMillis(normalPollingIntervalMillis);
when(mMockNtpTrustedTime.forceRefresh(mDummyNetwork)).thenReturn(false);
RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
@@ -392,13 +555,6 @@ public class NetworkTimeUpdateServiceTest {
verify(mockCallback, never()).submitSuggestion(any());
}
private long calculateRefreshDelayMillisForTimeResult(NtpTrustedTime.TimeResult timeResult,
int normalPollingIntervalMillis) {
long currentElapsedRealtimeMillis = mFakeElapsedRealtimeClock.getElapsedRealtimeMillis();
long timeResultAgeMillis = timeResult.getAgeMillis(currentElapsedRealtimeMillis);
return normalPollingIntervalMillis - timeResultAgeMillis;
}
private static NetworkTimeSuggestion createExpectedSuggestion(
NtpTrustedTime.TimeResult timeResult) {
UnixEpochTime unixEpochTime = new UnixEpochTime(