Merge "Add lower bound checks for non-manual time suggestions."

This commit is contained in:
Almaz Mingaleev
2020-11-26 09:54:50 +00:00
committed by Gerrit Code Review
3 changed files with 185 additions and 67 deletions

View File

@@ -20,12 +20,15 @@ import android.annotation.NonNull;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.provider.Settings; import android.provider.Settings;
import android.util.Slog; import android.util.Slog;
import java.time.Instant;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -37,6 +40,13 @@ public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrat
private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000; private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000;
/**
* Time in the past. If automatic time suggestion is before this point, it's
* incorrect for sure.
*/
private static final Instant TIME_LOWER_BOUND = Instant.ofEpochMilli(
Long.max(Environment.getRootDirectory().lastModified(), Build.TIME));
/** /**
* If a newly calculated system clock time and the current system clock time differs by this or * If a newly calculated system clock time and the current system clock time differs by this or
* more the system clock will actually be updated. Used to prevent the system clock being set * more the system clock will actually be updated. Used to prevent the system clock being set
@@ -78,6 +88,11 @@ public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrat
} }
} }
@Override
public Instant autoTimeLowerBound() {
return TIME_LOWER_BOUND;
}
@Override @Override
public void acquireWakeLock() { public void acquireWakeLock() {
if (mWakeLock.isHeld()) { if (mWakeLock.isHeld()) {

View File

@@ -36,6 +36,7 @@ import com.android.server.timezonedetector.ReferenceWithHistory;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.time.Instant;
/** /**
* An implementation of {@link TimeDetectorStrategy} that passes telephony and manual suggestions to * An implementation of {@link TimeDetectorStrategy} that passes telephony and manual suggestions to
@@ -143,6 +144,15 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
/** Returns true if automatic time detection is enabled. */ /** Returns true if automatic time detection is enabled. */
boolean isAutoTimeDetectionEnabled(); boolean isAutoTimeDetectionEnabled();
/**
* Returns a lower bound for valid automatic times. It is guaranteed to be in the past,
* i.e. it is unrelated to the current system clock time.
* It holds no other meaning; it could be related to when the device system image was built,
* or could be updated by a mainline module.
*/
@NonNull
Instant autoTimeLowerBound();
/** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */ /** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */
void acquireWakeLock(); void acquireWakeLock();
@@ -177,7 +187,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
@Override @Override
public synchronized void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion) { public synchronized void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion) {
if (!validateSuggestionTime(timeSuggestion.getUtcTime(), timeSuggestion)) { if (!validateAutoSuggestionTime(timeSuggestion.getUtcTime(), timeSuggestion)) {
return; return;
} }
@@ -211,9 +221,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
return; return;
} }
// Perform validation / input filtering and record the validated suggestion against the if (!validateAutoSuggestionTime(timeSuggestion.getUtcTime(), timeSuggestion)) {
// slotIndex. return;
if (!validateAndStoreTelephonySuggestion(timeSuggestion)) { }
// Perform input filtering and record the validated suggestion against the slotIndex.
if (!storeTelephonySuggestion(timeSuggestion)) {
return; return;
} }
@@ -272,14 +285,9 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
} }
@GuardedBy("this") @GuardedBy("this")
private boolean validateAndStoreTelephonySuggestion( private boolean storeTelephonySuggestion(
@NonNull TelephonyTimeSuggestion suggestion) { @NonNull TelephonyTimeSuggestion suggestion) {
TimestampedValue<Long> newUtcTime = suggestion.getUtcTime(); TimestampedValue<Long> newUtcTime = suggestion.getUtcTime();
if (!validateSuggestionTime(newUtcTime, suggestion)) {
// There's probably nothing useful we can do: elsewhere we assume that reference
// times are in the past so just stop here.
return false;
}
int slotIndex = suggestion.getSlotIndex(); int slotIndex = suggestion.getSlotIndex();
TelephonyTimeSuggestion previousSuggestion = mSuggestionBySlotIndex.get(slotIndex); TelephonyTimeSuggestion previousSuggestion = mSuggestionBySlotIndex.get(slotIndex);
@@ -330,6 +338,26 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
return true; return true;
} }
private boolean validateAutoSuggestionTime(
@NonNull TimestampedValue<Long> newUtcTime, @NonNull Object suggestion) {
return validateSuggestionTime(newUtcTime, suggestion)
&& validateSuggestionAgainstLowerBound(newUtcTime, suggestion);
}
private boolean validateSuggestionAgainstLowerBound(
@NonNull TimestampedValue<Long> newUtcTime, @NonNull Object suggestion) {
Instant lowerBound = mCallback.autoTimeLowerBound();
// Suggestion is definitely wrong if it comes before lower time bound.
if (lowerBound.isAfter(Instant.ofEpochMilli(newUtcTime.getValue()))) {
Slog.w(LOG_TAG, "Suggestion points to time before lower bound, skipping it. "
+ "suggestion=" + suggestion + ", lower bound=" + lowerBound);
return false;
}
return true;
}
@GuardedBy("this") @GuardedBy("this")
private void doAutoTimeDetection(@NonNull String detectionReason) { private void doAutoTimeDetection(@NonNull String detectionReason) {
if (!mCallback.isAutoTimeDetectionEnabled()) { if (!mCallback.isAutoTimeDetectionEnabled()) {

View File

@@ -25,9 +25,6 @@ import static org.junit.Assert.fail;
import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion;
import android.icu.util.Calendar;
import android.icu.util.GregorianCalendar;
import android.icu.util.TimeZone;
import android.os.TimestampedValue; import android.os.TimestampedValue;
import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4;
@@ -37,20 +34,25 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import java.time.Duration; import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class TimeDetectorStrategyImplTest { public class TimeDetectorStrategyImplTest {
private static final TimestampedValue<Long> ARBITRARY_CLOCK_INITIALIZATION_INFO = private static final Instant TIME_LOWER_BOUND = createUtcTime(2009, 1, 1, 12, 0, 0);
private static final TimestampedValue<Instant> ARBITRARY_CLOCK_INITIALIZATION_INFO =
new TimestampedValue<>( new TimestampedValue<>(
123456789L /* realtimeClockMillis */, 123456789L /* realtimeClockMillis */,
createUtcTime(2008, 5, 23, 12, 0, 0)); createUtcTime(2010, 5, 23, 12, 0, 0));
/** /**
* An arbitrary time, very different from the {@link #ARBITRARY_CLOCK_INITIALIZATION_INFO} * An arbitrary time, very different from the {@link #ARBITRARY_CLOCK_INITIALIZATION_INFO}
* time. Can be used as the basis for time suggestions. * time. Can be used as the basis for time suggestions.
*/ */
private static final long ARBITRARY_TEST_TIME_MILLIS = createUtcTime(2018, 1, 1, 12, 0, 0); private static final Instant ARBITRARY_TEST_TIME = createUtcTime(2018, 1, 1, 12, 0, 0);
private static final int ARBITRARY_SLOT_INDEX = 123456; private static final int ARBITRARY_SLOT_INDEX = 123456;
@@ -67,10 +69,10 @@ public class TimeDetectorStrategyImplTest {
.pokeAutoTimeDetectionEnabled(true); .pokeAutoTimeDetectionEnabled(true);
int slotIndex = ARBITRARY_SLOT_INDEX; int slotIndex = ARBITRARY_SLOT_INDEX;
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; Instant testTime = ARBITRARY_TEST_TIME;
TelephonyTimeSuggestion timeSuggestion = TelephonyTimeSuggestion timeSuggestion =
mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis); mScript.generateTelephonyTimeSuggestion(slotIndex, testTime);
mScript.simulateTimePassing() mScript.simulateTimePassing()
.simulateTelephonyTimeSuggestion(timeSuggestion); .simulateTelephonyTimeSuggestion(timeSuggestion);
@@ -106,9 +108,9 @@ public class TimeDetectorStrategyImplTest {
// Send the first time signal. It should be used. // Send the first time signal. It should be used.
{ {
TelephonyTimeSuggestion timeSuggestion1 = TelephonyTimeSuggestion timeSuggestion1 =
mScript.generateTelephonyTimeSuggestion(slotIndex, ARBITRARY_TEST_TIME_MILLIS); mScript.generateTelephonyTimeSuggestion(slotIndex, ARBITRARY_TEST_TIME);
// Increment the the device clocks to simulate the passage of time. // Increment the device clocks to simulate the passage of time.
mScript.simulateTimePassing(clockIncrementMillis); mScript.simulateTimePassing(clockIncrementMillis);
long expectedSystemClockMillis1 = long expectedSystemClockMillis1 =
@@ -157,13 +159,13 @@ public class TimeDetectorStrategyImplTest {
// uses the lowest slotIndex when multiple telephony suggestions are available. // uses the lowest slotIndex when multiple telephony suggestions are available.
int slotIndex1 = ARBITRARY_SLOT_INDEX; int slotIndex1 = ARBITRARY_SLOT_INDEX;
int slotIndex2 = ARBITRARY_SLOT_INDEX + 1; int slotIndex2 = ARBITRARY_SLOT_INDEX + 1;
long slotIndex1TimeMillis = ARBITRARY_TEST_TIME_MILLIS; Instant slotIndex1Time = ARBITRARY_TEST_TIME;
long slotIndex2TimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(1).toMillis(); Instant slotIndex2Time = ARBITRARY_TEST_TIME.plus(Duration.ofDays(1));
// Make a suggestion with slotIndex2. // Make a suggestion with slotIndex2.
{ {
TelephonyTimeSuggestion slotIndex2TimeSuggestion = TelephonyTimeSuggestion slotIndex2TimeSuggestion =
mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2TimeMillis); mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2Time);
mScript.simulateTimePassing(); mScript.simulateTimePassing();
long expectedSystemClockMillis = long expectedSystemClockMillis =
@@ -180,7 +182,7 @@ public class TimeDetectorStrategyImplTest {
// Now make a different suggestion with slotIndex1. // Now make a different suggestion with slotIndex1.
{ {
TelephonyTimeSuggestion slotIndex1TimeSuggestion = TelephonyTimeSuggestion slotIndex1TimeSuggestion =
mScript.generateTelephonyTimeSuggestion(slotIndex1, slotIndex1TimeMillis); mScript.generateTelephonyTimeSuggestion(slotIndex1, slotIndex1Time);
mScript.simulateTimePassing(); mScript.simulateTimePassing();
long expectedSystemClockMillis = long expectedSystemClockMillis =
@@ -198,7 +200,7 @@ public class TimeDetectorStrategyImplTest {
// slotIndex1 suggestion will still "win". // slotIndex1 suggestion will still "win".
{ {
TelephonyTimeSuggestion slotIndex2TimeSuggestion = TelephonyTimeSuggestion slotIndex2TimeSuggestion =
mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2TimeMillis); mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2Time);
mScript.simulateTimePassing(); mScript.simulateTimePassing();
mScript.simulateTelephonyTimeSuggestion(slotIndex2TimeSuggestion) mScript.simulateTelephonyTimeSuggestion(slotIndex2TimeSuggestion)
@@ -213,7 +215,7 @@ public class TimeDetectorStrategyImplTest {
// is in an older "bucket". // is in an older "bucket".
{ {
TelephonyTimeSuggestion slotIndex2TimeSuggestion = TelephonyTimeSuggestion slotIndex2TimeSuggestion =
mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2TimeMillis); mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2Time);
mScript.simulateTimePassing(); mScript.simulateTimePassing();
long expectedSystemClockMillis = long expectedSystemClockMillis =
@@ -232,7 +234,7 @@ public class TimeDetectorStrategyImplTest {
int slotIndex = ARBITRARY_SLOT_INDEX; int slotIndex = ARBITRARY_SLOT_INDEX;
TelephonyTimeSuggestion timeSuggestion = TelephonyTimeSuggestion timeSuggestion =
mScript.generateTelephonyTimeSuggestion(slotIndex, ARBITRARY_TEST_TIME_MILLIS); mScript.generateTelephonyTimeSuggestion(slotIndex, ARBITRARY_TEST_TIME);
mScript.simulateTimePassing() mScript.simulateTimePassing()
.simulateTelephonyTimeSuggestion(timeSuggestion) .simulateTelephonyTimeSuggestion(timeSuggestion)
.verifySystemClockWasNotSetAndResetCallTracking() .verifySystemClockWasNotSetAndResetCallTracking()
@@ -246,11 +248,11 @@ public class TimeDetectorStrategyImplTest {
.pokeThresholds(systemClockUpdateThreshold) .pokeThresholds(systemClockUpdateThreshold)
.pokeAutoTimeDetectionEnabled(true); .pokeAutoTimeDetectionEnabled(true);
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; Instant testTime = ARBITRARY_TEST_TIME;
int slotIndex = ARBITRARY_SLOT_INDEX; int slotIndex = ARBITRARY_SLOT_INDEX;
TelephonyTimeSuggestion timeSuggestion1 = TelephonyTimeSuggestion timeSuggestion1 =
mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis); mScript.generateTelephonyTimeSuggestion(slotIndex, testTime);
TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime(); TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
// Initialize the strategy / device with a time set from a telephony suggestion. // Initialize the strategy / device with a time set from a telephony suggestion.
@@ -299,6 +301,23 @@ public class TimeDetectorStrategyImplTest {
.assertLatestTelephonySuggestion(slotIndex, timeSuggestion4); .assertLatestTelephonySuggestion(slotIndex, timeSuggestion4);
} }
@Test
public void telephonyTimeSuggestion_ignoredWhenReferencedTimeIsInThePast() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true);
int slotIndex = ARBITRARY_SLOT_INDEX;
Instant suggestedTime = TIME_LOWER_BOUND.minus(Duration.ofDays(1));
TelephonyTimeSuggestion timeSuggestion =
mScript.generateTelephonyTimeSuggestion(
slotIndex, suggestedTime);
mScript.simulateTelephonyTimeSuggestion(timeSuggestion)
.verifySystemClockWasNotSetAndResetCallTracking()
.assertLatestTelephonySuggestion(slotIndex, null);
}
@Test @Test
public void testSuggestTelephonyTime_timeDetectionToggled() { public void testSuggestTelephonyTime_timeDetectionToggled() {
final int clockIncrementMillis = 100; final int clockIncrementMillis = 100;
@@ -308,9 +327,9 @@ public class TimeDetectorStrategyImplTest {
.pokeAutoTimeDetectionEnabled(false); .pokeAutoTimeDetectionEnabled(false);
int slotIndex = ARBITRARY_SLOT_INDEX; int slotIndex = ARBITRARY_SLOT_INDEX;
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; Instant testTime = ARBITRARY_TEST_TIME;
TelephonyTimeSuggestion timeSuggestion1 = TelephonyTimeSuggestion timeSuggestion1 =
mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis); mScript.generateTelephonyTimeSuggestion(slotIndex, testTime);
TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime(); TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
// Simulate time passing. // Simulate time passing.
@@ -366,9 +385,9 @@ public class TimeDetectorStrategyImplTest {
.pokeAutoTimeDetectionEnabled(true); .pokeAutoTimeDetectionEnabled(true);
int slotIndex = ARBITRARY_SLOT_INDEX; int slotIndex = ARBITRARY_SLOT_INDEX;
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; Instant testTime = ARBITRARY_TEST_TIME;
TelephonyTimeSuggestion telephonySuggestion = TelephonyTimeSuggestion telephonySuggestion =
mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis); mScript.generateTelephonyTimeSuggestion(slotIndex, testTime);
mScript.simulateTimePassing(); mScript.simulateTimePassing();
@@ -397,7 +416,7 @@ public class TimeDetectorStrategyImplTest {
.pokeAutoTimeDetectionEnabled(false); .pokeAutoTimeDetectionEnabled(false);
ManualTimeSuggestion timeSuggestion = ManualTimeSuggestion timeSuggestion =
mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS); mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME);
mScript.simulateTimePassing(); mScript.simulateTimePassing();
@@ -416,9 +435,9 @@ public class TimeDetectorStrategyImplTest {
int slotIndex = ARBITRARY_SLOT_INDEX; int slotIndex = ARBITRARY_SLOT_INDEX;
// Simulate a telephony suggestion. // Simulate a telephony suggestion.
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; Instant testTime = ARBITRARY_TEST_TIME;
TelephonyTimeSuggestion telephonyTimeSuggestion = TelephonyTimeSuggestion telephonyTimeSuggestion =
mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis); mScript.generateTelephonyTimeSuggestion(slotIndex, testTime);
// Simulate the passage of time. // Simulate the passage of time.
mScript.simulateTimePassing(); mScript.simulateTimePassing();
@@ -441,9 +460,9 @@ public class TimeDetectorStrategyImplTest {
mScript.simulateTimePassing(); mScript.simulateTimePassing();
// Simulate a manual suggestion 1 day different from the auto suggestion. // Simulate a manual suggestion 1 day different from the auto suggestion.
long manualTimeMillis = testTimeMillis + Duration.ofDays(1).toMillis(); Instant manualTime = testTime.plus(Duration.ofDays(1));
ManualTimeSuggestion manualTimeSuggestion = ManualTimeSuggestion manualTimeSuggestion =
mScript.generateManualTimeSuggestion(manualTimeMillis); mScript.generateManualTimeSuggestion(manualTime);
mScript.simulateTimePassing(); mScript.simulateTimePassing();
long expectedManualClockMillis = long expectedManualClockMillis =
@@ -478,20 +497,33 @@ public class TimeDetectorStrategyImplTest {
.pokeAutoTimeDetectionEnabled(true); .pokeAutoTimeDetectionEnabled(true);
ManualTimeSuggestion timeSuggestion = ManualTimeSuggestion timeSuggestion =
mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS); mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME);
mScript.simulateTimePassing() mScript.simulateTimePassing()
.simulateManualTimeSuggestion(timeSuggestion, false /* expectedResult */) .simulateManualTimeSuggestion(timeSuggestion, false /* expectedResult */)
.verifySystemClockWasNotSetAndResetCallTracking(); .verifySystemClockWasNotSetAndResetCallTracking();
} }
@Test
public void suggestManualTime_ignoresTimeLowerBound() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(false);
Instant suggestedTime = TIME_LOWER_BOUND.minus(Duration.ofDays(1));
ManualTimeSuggestion timeSuggestion =
mScript.generateManualTimeSuggestion(suggestedTime);
mScript.simulateManualTimeSuggestion(timeSuggestion, true /* expectedResult */)
.verifySystemClockWasSetAndResetCallTracking(suggestedTime.toEpochMilli());
}
@Test @Test
public void testSuggestNetworkTime_autoTimeEnabled() { public void testSuggestNetworkTime_autoTimeEnabled() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true); .pokeAutoTimeDetectionEnabled(true);
NetworkTimeSuggestion timeSuggestion = NetworkTimeSuggestion timeSuggestion =
mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS); mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME);
mScript.simulateTimePassing(); mScript.simulateTimePassing();
@@ -507,7 +539,7 @@ public class TimeDetectorStrategyImplTest {
.pokeAutoTimeDetectionEnabled(false); .pokeAutoTimeDetectionEnabled(false);
NetworkTimeSuggestion timeSuggestion = NetworkTimeSuggestion timeSuggestion =
mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS); mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME);
mScript.simulateTimePassing() mScript.simulateTimePassing()
.simulateNetworkTimeSuggestion(timeSuggestion) .simulateNetworkTimeSuggestion(timeSuggestion)
@@ -520,16 +552,16 @@ public class TimeDetectorStrategyImplTest {
.pokeAutoTimeDetectionEnabled(true); .pokeAutoTimeDetectionEnabled(true);
// Three obviously different times that could not be mistaken for each other. // Three obviously different times that could not be mistaken for each other.
long networkTimeMillis1 = ARBITRARY_TEST_TIME_MILLIS; Instant networkTime1 = ARBITRARY_TEST_TIME;
long networkTimeMillis2 = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(30).toMillis(); Instant networkTime2 = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30));
long telephonyTimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(60).toMillis(); Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(60));
// A small increment used to simulate the passage of time, but not enough to interfere with // A small increment used to simulate the passage of time, but not enough to interfere with
// macro-level time changes associated with suggestion age. // macro-level time changes associated with suggestion age.
final long smallTimeIncrementMillis = 101; final long smallTimeIncrementMillis = 101;
// A network suggestion is made. It should be used because there is no telephony suggestion. // A network suggestion is made. It should be used because there is no telephony suggestion.
NetworkTimeSuggestion networkTimeSuggestion1 = NetworkTimeSuggestion networkTimeSuggestion1 =
mScript.generateNetworkTimeSuggestion(networkTimeMillis1); mScript.generateNetworkTimeSuggestion(networkTime1);
mScript.simulateTimePassing(smallTimeIncrementMillis) mScript.simulateTimePassing(smallTimeIncrementMillis)
.simulateNetworkTimeSuggestion(networkTimeSuggestion1) .simulateNetworkTimeSuggestion(networkTimeSuggestion1)
.verifySystemClockWasSetAndResetCallTracking( .verifySystemClockWasSetAndResetCallTracking(
@@ -548,7 +580,7 @@ public class TimeDetectorStrategyImplTest {
// Now a telephony suggestion is made. Telephony suggestions are prioritized over network // Now a telephony suggestion is made. Telephony suggestions are prioritized over network
// suggestions so it should "win". // suggestions so it should "win".
TelephonyTimeSuggestion telephonyTimeSuggestion = TelephonyTimeSuggestion telephonyTimeSuggestion =
mScript.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeMillis); mScript.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, telephonyTime);
mScript.simulateTimePassing(smallTimeIncrementMillis) mScript.simulateTimePassing(smallTimeIncrementMillis)
.simulateTelephonyTimeSuggestion(telephonyTimeSuggestion) .simulateTelephonyTimeSuggestion(telephonyTimeSuggestion)
.verifySystemClockWasSetAndResetCallTracking( .verifySystemClockWasSetAndResetCallTracking(
@@ -568,7 +600,7 @@ public class TimeDetectorStrategyImplTest {
// Now another network suggestion is made. Telephony suggestions are prioritized over // Now another network suggestion is made. Telephony suggestions are prioritized over
// network suggestions so the latest telephony suggestion should still "win". // network suggestions so the latest telephony suggestion should still "win".
NetworkTimeSuggestion networkTimeSuggestion2 = NetworkTimeSuggestion networkTimeSuggestion2 =
mScript.generateNetworkTimeSuggestion(networkTimeMillis2); mScript.generateNetworkTimeSuggestion(networkTime2);
mScript.simulateTimePassing(smallTimeIncrementMillis) mScript.simulateTimePassing(smallTimeIncrementMillis)
.simulateNetworkTimeSuggestion(networkTimeSuggestion2) .simulateNetworkTimeSuggestion(networkTimeSuggestion2)
.verifySystemClockWasNotSetAndResetCallTracking(); .verifySystemClockWasNotSetAndResetCallTracking();
@@ -612,6 +644,20 @@ public class TimeDetectorStrategyImplTest {
assertNull(mScript.peekBestTelephonySuggestion()); assertNull(mScript.peekBestTelephonySuggestion());
} }
@Test
public void networkTimeSuggestion_ignoredWhenReferencedTimeIsInThePast() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true);
Instant suggestedTime = TIME_LOWER_BOUND.minus(Duration.ofDays(1));
NetworkTimeSuggestion timeSuggestion = mScript
.generateNetworkTimeSuggestion(suggestedTime);
mScript.simulateNetworkTimeSuggestion(timeSuggestion)
.verifySystemClockWasNotSetAndResetCallTracking()
.assertLatestNetworkSuggestion(null);
}
/** /**
* A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
* like the real thing should, it also asserts preconditions. * like the real thing should, it also asserts preconditions.
@@ -636,6 +682,11 @@ public class TimeDetectorStrategyImplTest {
return mAutoTimeDetectionEnabled; return mAutoTimeDetectionEnabled;
} }
@Override
public Instant autoTimeLowerBound() {
return TIME_LOWER_BOUND;
}
@Override @Override
public void acquireWakeLock() { public void acquireWakeLock() {
if (mWakeLockAcquired) { if (mWakeLockAcquired) {
@@ -703,7 +754,10 @@ public class TimeDetectorStrategyImplTest {
} }
void verifySystemClockNotSet() { void verifySystemClockNotSet() {
assertFalse(mSystemClockWasSet); assertFalse(
String.format("System clock was manipulated and set to %s(=%s)",
Instant.ofEpochMilli(mSystemClockMillis), mSystemClockMillis),
mSystemClockWasSet);
} }
void verifySystemClockWasSet(long expectedSystemClockMillis) { void verifySystemClockWasSet(long expectedSystemClockMillis) {
@@ -739,9 +793,9 @@ public class TimeDetectorStrategyImplTest {
return this; return this;
} }
Script pokeFakeClocks(TimestampedValue<Long> timeInfo) { Script pokeFakeClocks(TimestampedValue<Instant> timeInfo) {
mFakeCallback.pokeElapsedRealtimeMillis(timeInfo.getReferenceTimeMillis()); mFakeCallback.pokeElapsedRealtimeMillis(timeInfo.getReferenceTimeMillis());
mFakeCallback.pokeSystemClockMillis(timeInfo.getValue()); mFakeCallback.pokeSystemClockMillis(timeInfo.getValue().toEpochMilli());
return this; return this;
} }
@@ -765,7 +819,13 @@ public class TimeDetectorStrategyImplTest {
Script simulateManualTimeSuggestion( Script simulateManualTimeSuggestion(
ManualTimeSuggestion timeSuggestion, boolean expectedResult) { ManualTimeSuggestion timeSuggestion, boolean expectedResult) {
assertEquals(expectedResult, mTimeDetectorStrategy.suggestManualTime(timeSuggestion)); String errorMessage = expectedResult
? "Manual time suggestion was ignored, but expected to be accepted."
: "Manual time suggestion was accepted, but expected to be ignored.";
assertEquals(
errorMessage,
expectedResult,
mTimeDetectorStrategy.suggestManualTime(timeSuggestion));
return this; return this;
} }
@@ -808,7 +868,10 @@ public class TimeDetectorStrategyImplTest {
* White box test info: Asserts the latest suggestion for the slotIndex is as expected. * White box test info: Asserts the latest suggestion for the slotIndex is as expected.
*/ */
Script assertLatestTelephonySuggestion(int slotIndex, TelephonyTimeSuggestion expected) { Script assertLatestTelephonySuggestion(int slotIndex, TelephonyTimeSuggestion expected) {
assertEquals(expected, mTimeDetectorStrategy.getLatestTelephonySuggestion(slotIndex)); assertEquals(
"Expected to see " + expected + " at slotIndex=" + slotIndex + ", but got "
+ mTimeDetectorStrategy.getLatestTelephonySuggestion(slotIndex),
expected, mTimeDetectorStrategy.getLatestTelephonySuggestion(slotIndex));
return this; return this;
} }
@@ -840,9 +903,11 @@ public class TimeDetectorStrategyImplTest {
* Generates a ManualTimeSuggestion using the current elapsed realtime clock for the * Generates a ManualTimeSuggestion using the current elapsed realtime clock for the
* reference time. * reference time.
*/ */
ManualTimeSuggestion generateManualTimeSuggestion(long timeMillis) { ManualTimeSuggestion generateManualTimeSuggestion(Instant suggestedTime) {
TimestampedValue<Long> utcTime = TimestampedValue<Long> utcTime =
new TimestampedValue<>(mFakeCallback.peekElapsedRealtimeMillis(), timeMillis); new TimestampedValue<>(
mFakeCallback.peekElapsedRealtimeMillis(),
suggestedTime.toEpochMilli());
return new ManualTimeSuggestion(utcTime); return new ManualTimeSuggestion(utcTime);
} }
@@ -850,21 +915,33 @@ public class TimeDetectorStrategyImplTest {
* Generates a {@link TelephonyTimeSuggestion} using the current elapsed realtime clock for * Generates a {@link TelephonyTimeSuggestion} using the current elapsed realtime clock for
* the reference time. * the reference time.
*/ */
TelephonyTimeSuggestion generateTelephonyTimeSuggestion(int slotIndex, Long timeMillis) { TelephonyTimeSuggestion generateTelephonyTimeSuggestion(int slotIndex, long timeMillis) {
TimestampedValue<Long> time = null; TimestampedValue<Long> time =
if (timeMillis != null) { new TimestampedValue<>(peekElapsedRealtimeMillis(), timeMillis);
time = new TimestampedValue<>(peekElapsedRealtimeMillis(), timeMillis);
}
return createTelephonyTimeSuggestion(slotIndex, time); return createTelephonyTimeSuggestion(slotIndex, time);
} }
/**
* Generates a {@link TelephonyTimeSuggestion} using the current elapsed realtime clock for
* the reference time.
*/
TelephonyTimeSuggestion generateTelephonyTimeSuggestion(
int slotIndex, Instant suggestedTime) {
if (suggestedTime == null) {
return createTelephonyTimeSuggestion(slotIndex, null);
}
return generateTelephonyTimeSuggestion(slotIndex, suggestedTime.toEpochMilli());
}
/** /**
* Generates a NetworkTimeSuggestion using the current elapsed realtime clock for the * Generates a NetworkTimeSuggestion using the current elapsed realtime clock for the
* reference time. * reference time.
*/ */
NetworkTimeSuggestion generateNetworkTimeSuggestion(long timeMillis) { NetworkTimeSuggestion generateNetworkTimeSuggestion(Instant suggestedTime) {
TimestampedValue<Long> utcTime = TimestampedValue<Long> utcTime =
new TimestampedValue<>(mFakeCallback.peekElapsedRealtimeMillis(), timeMillis); new TimestampedValue<>(
mFakeCallback.peekElapsedRealtimeMillis(),
suggestedTime.toEpochMilli());
return new NetworkTimeSuggestion(utcTime); return new NetworkTimeSuggestion(utcTime);
} }
@@ -884,11 +961,9 @@ public class TimeDetectorStrategyImplTest {
.build(); .build();
} }
private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute, private static Instant createUtcTime(int year, int monthInYear, int day, int hourOfDay,
int second) { int minute, int second) {
Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC")); return LocalDateTime.of(year, monthInYear, day, hourOfDay, minute, second)
cal.clear(); .toInstant(ZoneOffset.UTC);
cal.set(year, monthInYear - 1, day, hourOfDay, minute, second);
return cal.getTimeInMillis();
} }
} }