Merge "Logging and readability improvements"

This commit is contained in:
Neil Fuller
2023-01-12 13:13:33 +00:00
committed by Android (Google) Code Review
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(