Merge "Add a path for GNSS time suggestions"

This commit is contained in:
Treehugger Robot
2020-12-14 18:02:50 +00:00
committed by Gerrit Code Review
11 changed files with 631 additions and 6 deletions

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.timedetector;
parcelable GnssTimeSuggestion;

View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.timedetector;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.TimestampedValue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* A time signal from a GNSS source.
*
* <p>{@code utcTime} is the suggested time. The {@code utcTime.value} is the number of milliseconds
* elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the value of the
* elapsed realtime clock when the {@code utcTime.value} was established.
* Note that the elapsed realtime clock is considered accurate but it is volatile, so time
* suggestions cannot be persisted across device resets.
*
* <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
* record why the suggestion exists and how it was entered. This information exists only to aid in
* debugging and therefore is used by {@link #toString()}, but it is not for use in detection
* logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
*
* @hide
*/
public final class GnssTimeSuggestion implements Parcelable {
public static final @NonNull Creator<GnssTimeSuggestion> CREATOR =
new Creator<GnssTimeSuggestion>() {
public GnssTimeSuggestion createFromParcel(Parcel in) {
return GnssTimeSuggestion.createFromParcel(in);
}
public GnssTimeSuggestion[] newArray(int size) {
return new GnssTimeSuggestion[size];
}
};
@NonNull private final TimestampedValue<Long> mUtcTime;
@Nullable private ArrayList<String> mDebugInfo;
public GnssTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) {
mUtcTime = Objects.requireNonNull(utcTime);
Objects.requireNonNull(utcTime.getValue());
}
private static GnssTimeSuggestion createFromParcel(Parcel in) {
TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
GnssTimeSuggestion suggestion = new GnssTimeSuggestion(utcTime);
@SuppressWarnings("unchecked")
ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
suggestion.mDebugInfo = debugInfo;
return suggestion;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mUtcTime, 0);
dest.writeList(mDebugInfo);
}
@NonNull
public TimestampedValue<Long> getUtcTime() {
return mUtcTime;
}
@NonNull
public List<String> getDebugInfo() {
return mDebugInfo == null
? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
}
/**
* Associates information with the instance that can be useful for debugging / logging. The
* information is present in {@link #toString()} but is not considered for
* {@link #equals(Object)} and {@link #hashCode()}.
*/
public void addDebugInfo(String... debugInfos) {
if (mDebugInfo == null) {
mDebugInfo = new ArrayList<>();
}
mDebugInfo.addAll(Arrays.asList(debugInfos));
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
GnssTimeSuggestion that = (GnssTimeSuggestion) o;
return Objects.equals(mUtcTime, that.mUtcTime);
}
@Override
public int hashCode() {
return Objects.hash(mUtcTime);
}
@Override
public String toString() {
return "GnssTimeSuggestion{"
+ "mUtcTime=" + mUtcTime
+ ", mDebugInfo=" + mDebugInfo
+ '}';
}
}

View File

@@ -16,6 +16,7 @@
package android.app.timedetector;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
@@ -34,6 +35,7 @@ import android.app.timedetector.TelephonyTimeSuggestion;
* {@hide}
*/
interface ITimeDetectorService {
void suggestGnssTime(in GnssTimeSuggestion timeSuggestion);
boolean suggestManualTime(in ManualTimeSuggestion timeSuggestion);
void suggestNetworkTime(in NetworkTimeSuggestion timeSuggestion);
void suggestTelephonyTime(in TelephonyTimeSuggestion timeSuggestion);

View File

@@ -71,4 +71,12 @@ public interface TimeDetector {
*/
@RequiresPermission(android.Manifest.permission.SET_TIME)
void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion);
/**
* Suggests the time according to a gnss time source.
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.SET_TIME)
void suggestGnssTime(GnssTimeSuggestion timeSuggestion);
}

View File

@@ -74,4 +74,16 @@ public final class TimeDetectorImpl implements TimeDetector {
throw e.rethrowFromSystemServer();
}
}
@Override
public void suggestGnssTime(GnssTimeSuggestion timeSuggestion) {
if (DEBUG) {
Log.d(TAG, "suggestGnssTime called: " + timeSuggestion);
}
try {
mITimeDetectorService.suggestGnssTime(timeSuggestion);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.timedetector;
import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import android.os.TimestampedValue;
import org.junit.Test;
public class GnssTimeSuggestionTest {
private static final TimestampedValue<Long> ARBITRARY_TIME =
new TimestampedValue<>(1111L, 2222L);
@Test
public void testEquals() {
GnssTimeSuggestion one = new GnssTimeSuggestion(ARBITRARY_TIME);
assertEquals(one, one);
GnssTimeSuggestion two = new GnssTimeSuggestion(ARBITRARY_TIME);
assertEquals(one, two);
assertEquals(two, one);
TimestampedValue<Long> differentTime = new TimestampedValue<>(
ARBITRARY_TIME.getReferenceTimeMillis() + 1,
ARBITRARY_TIME.getValue());
GnssTimeSuggestion three = new GnssTimeSuggestion(differentTime);
assertNotEquals(one, three);
assertNotEquals(three, one);
// DebugInfo must not be considered in equals().
one.addDebugInfo("Debug info 1");
two.addDebugInfo("Debug info 2");
assertEquals(one, two);
}
@Test
public void testParcelable() {
GnssTimeSuggestion suggestion = new GnssTimeSuggestion(ARBITRARY_TIME);
assertRoundTripParcelable(suggestion);
// DebugInfo should also be stored (but is not checked by equals()
suggestion.addDebugInfo("This is debug info");
GnssTimeSuggestion rtSuggestion = roundTripParcelable(suggestion);
assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo());
}
}

View File

@@ -18,6 +18,7 @@ package com.android.server.timedetector;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ITimeDetectorService;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
@@ -122,6 +123,14 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal));
}
@Override
public void suggestGnssTime(@NonNull GnssTimeSuggestion timeSignal) {
enforceSuggestGnssTimePermission();
Objects.requireNonNull(timeSignal);
mHandler.post(() -> mTimeDetectorStrategy.suggestGnssTime(timeSignal));
}
/** Internal method for handling the auto time setting being changed. */
@VisibleForTesting
public void handleAutoTimeDetectionChanged() {
@@ -153,4 +162,10 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
android.Manifest.permission.SET_TIME,
"set time");
}
private void enforceSuggestGnssTimePermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_TIME,
"suggest gnss time");
}
}

View File

@@ -19,6 +19,7 @@ package com.android.server.timedetector;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
@@ -40,7 +41,7 @@ import java.lang.annotation.RetentionPolicy;
*/
public interface TimeDetectorStrategy {
@IntDef({ ORIGIN_TELEPHONY, ORIGIN_MANUAL, ORIGIN_NETWORK })
@IntDef({ ORIGIN_TELEPHONY, ORIGIN_MANUAL, ORIGIN_NETWORK, ORIGIN_GNSS })
@Retention(RetentionPolicy.SOURCE)
@interface Origin {}
@@ -56,6 +57,10 @@ public interface TimeDetectorStrategy {
@Origin
int ORIGIN_NETWORK = 3;
/** Used when a time value originated from a gnss signal. */
@Origin
int ORIGIN_GNSS = 4;
/** Processes the suggested time from telephony sources. */
void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSuggestion);
@@ -70,6 +75,9 @@ public interface TimeDetectorStrategy {
/** Processes the suggested time from network sources. */
void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion);
/** Processes the suggested time from gnss sources. */
void suggestGnssTime(@NonNull GnssTimeSuggestion timeSuggestion);
/**
* Handles the auto-time configuration changing For example, when the auto-time setting is
* toggled on or off.
@@ -102,6 +110,8 @@ public interface TimeDetectorStrategy {
return "network";
case ORIGIN_TELEPHONY:
return "telephony";
case ORIGIN_GNSS:
return "gnss";
default:
throw new IllegalArgumentException("origin=" + origin);
}
@@ -119,6 +129,8 @@ public interface TimeDetectorStrategy {
return ORIGIN_NETWORK;
case "telephony":
return ORIGIN_TELEPHONY;
case "gnss":
return ORIGIN_GNSS;
default:
throw new IllegalArgumentException("originString=" + originString);
}

View File

@@ -23,6 +23,7 @@ import static java.util.stream.Collectors.joining;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
@@ -109,6 +110,10 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
private final ReferenceWithHistory<NetworkTimeSuggestion> mLastNetworkSuggestion =
new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
@GuardedBy("this")
private final ReferenceWithHistory<GnssTimeSuggestion> mLastGnssSuggestion =
new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
/**
* The interface used by the strategy to interact with the surrounding service.
*
@@ -165,6 +170,20 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
mCallback = callback;
}
@Override
public synchronized void suggestGnssTime(@NonNull GnssTimeSuggestion timeSuggestion) {
final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime();
if (!validateAutoSuggestionTime(newUtcTime, timeSuggestion)) {
return;
}
mLastGnssSuggestion.set(timeSuggestion);
String reason = "GNSS time suggestion received: suggestion=" + timeSuggestion;
doAutoTimeDetection(reason);
}
@Override
public synchronized boolean suggestManualTime(@NonNull ManualTimeSuggestion suggestion) {
final TimestampedValue<Long> newUtcTime = suggestion.getUtcTime();
@@ -280,6 +299,11 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
mLastNetworkSuggestion.dump(ipw);
ipw.decreaseIndent(); // level 2
ipw.println("Gnss suggestion history:");
ipw.increaseIndent(); // level 2
mLastGnssSuggestion.dump(ipw);
ipw.decreaseIndent(); // level 2
ipw.decreaseIndent(); // level 1
ipw.flush();
}
@@ -386,6 +410,14 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
+ ", networkSuggestion=" + networkSuggestion
+ ", detectionReason=" + detectionReason;
}
} else if (origin == ORIGIN_GNSS) {
GnssTimeSuggestion gnssTimeSuggestion = findLatestValidGnssSuggestion();
if (gnssTimeSuggestion != null) {
newUtcTime = gnssTimeSuggestion.getUtcTime();
cause = "Found good gnss suggestion."
+ ", gnssTimeSuggestion=" + gnssTimeSuggestion
+ ", detectionReason=" + detectionReason;
}
} else {
Slog.w(LOG_TAG, "Unknown or unsupported origin=" + origin
+ " in " + Arrays.toString(originPriorities)
@@ -527,6 +559,26 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
return networkSuggestion;
}
/** Returns the latest, valid, gnss suggestion. Returns {@code null} if there isn't one. */
@GuardedBy("this")
@Nullable
private GnssTimeSuggestion findLatestValidGnssSuggestion() {
GnssTimeSuggestion gnssTimeSuggestion = mLastGnssSuggestion.get();
if (gnssTimeSuggestion == null) {
// No gnss suggestions received. This is normal if there's no gnss signal.
return null;
}
TimestampedValue<Long> utcTime = gnssTimeSuggestion.getUtcTime();
long elapsedRealTimeMillis = mCallback.elapsedRealtimeMillis();
if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) {
// The latest suggestion is not valid, usually due to its age.
return null;
}
return gnssTimeSuggestion;
}
@GuardedBy("this")
private boolean setSystemClockIfRequired(
@Origin int origin, @NonNull TimestampedValue<Long> time, @NonNull String cause) {
@@ -654,6 +706,16 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
return findLatestValidNetworkSuggestion();
}
/**
* Returns the latest valid gnss suggestion. Not intended for general use: it is used during
* tests to check strategy behavior.
*/
@VisibleForTesting
@Nullable
public synchronized GnssTimeSuggestion findLatestValidGnssSuggestionForTests() {
return findLatestValidGnssSuggestion();
}
/**
* A method used to inspect state during tests. Not intended for general use.
*/
@@ -672,6 +734,15 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
return mLastNetworkSuggestion.get();
}
/**
* A method used to inspect state during tests. Not intended for general use.
*/
@VisibleForTesting
@Nullable
public synchronized GnssTimeSuggestion getLatestGnssSuggestion() {
return mLastGnssSuggestion.get();
}
private static boolean validateSuggestionUtcTime(
long elapsedRealtimeMillis, TimestampedValue<Long> utcTime) {
long referenceTimeMillis = utcTime.getReferenceTimeMillis();

View File

@@ -28,6 +28,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
@@ -172,6 +173,36 @@ public class TimeDetectorServiceTest {
mStubbedTimeDetectorStrategy.verifySuggestNetworkTimeCalled(NetworkTimeSuggestion);
}
@Test(expected = SecurityException.class)
public void testSuggestGnssTime_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
GnssTimeSuggestion gnssTimeSuggestion = createGnssTimeSuggestion();
try {
mTimeDetectorService.suggestGnssTime(gnssTimeSuggestion);
fail();
} finally {
verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.SET_TIME), anyString());
}
}
@Test
public void testSuggestGnssTime() throws Exception {
doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
GnssTimeSuggestion gnssTimeSuggestion = createGnssTimeSuggestion();
mTimeDetectorService.suggestGnssTime(gnssTimeSuggestion);
mTestHandler.assertTotalMessagesEnqueued(1);
verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.SET_TIME), anyString());
mTestHandler.waitForMessagesToBeProcessed();
mStubbedTimeDetectorStrategy.verifySuggestGnssTimeCalled(gnssTimeSuggestion);
}
@Test
public void testDump() {
when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
@@ -216,12 +247,18 @@ public class TimeDetectorServiceTest {
return new NetworkTimeSuggestion(timeValue);
}
private static GnssTimeSuggestion createGnssTimeSuggestion() {
TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
return new GnssTimeSuggestion(timeValue);
}
private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy {
// Call tracking.
private TelephonyTimeSuggestion mLastTelephonySuggestion;
private ManualTimeSuggestion mLastManualSuggestion;
private NetworkTimeSuggestion mLastNetworkSuggestion;
private GnssTimeSuggestion mLastGnssSuggestion;
private boolean mHandleAutoTimeDetectionChangedCalled;
private boolean mDumpCalled;
@@ -241,6 +278,11 @@ public class TimeDetectorServiceTest {
mLastNetworkSuggestion = timeSuggestion;
}
@Override
public void suggestGnssTime(GnssTimeSuggestion timeSuggestion) {
mLastGnssSuggestion = timeSuggestion;
}
@Override
public void handleAutoTimeConfigChanged() {
mHandleAutoTimeDetectionChangedCalled = true;
@@ -255,6 +297,7 @@ public class TimeDetectorServiceTest {
mLastTelephonySuggestion = null;
mLastManualSuggestion = null;
mLastNetworkSuggestion = null;
mLastGnssSuggestion = null;
mHandleAutoTimeDetectionChangedCalled = false;
mDumpCalled = false;
}
@@ -271,6 +314,10 @@ public class TimeDetectorServiceTest {
assertEquals(expectedSuggestion, mLastNetworkSuggestion);
}
void verifySuggestGnssTimeCalled(GnssTimeSuggestion expectedSuggestion) {
assertEquals(expectedSuggestion, mLastGnssSuggestion);
}
void verifyHandleAutoTimeDetectionChangedCalled() {
assertTrue(mHandleAutoTimeDetectionChangedCalled);
}

View File

@@ -16,6 +16,7 @@
package com.android.server.timedetector;
import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_GNSS;
import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK;
import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY;
@@ -25,6 +26,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
@@ -569,7 +571,53 @@ public class TimeDetectorStrategyImplTest {
}
@Test
public void highPrioritySuggestionsShouldBeatLowerPrioritySuggestions() {
public void testSuggestGnssTime_autoTimeEnabled() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoOriginPriorities(ORIGIN_GNSS)
.pokeAutoTimeDetectionEnabled(true);
GnssTimeSuggestion timeSuggestion =
mScript.generateGnssTimeSuggestion(ARBITRARY_TEST_TIME);
mScript.simulateTimePassing();
long expectedSystemClockMillis =
mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
mScript.simulateGnssTimeSuggestion(timeSuggestion)
.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis);
}
@Test
public void testSuggestGnssTime_autoTimeDisabled() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoOriginPriorities(ORIGIN_GNSS)
.pokeAutoTimeDetectionEnabled(false);
GnssTimeSuggestion timeSuggestion =
mScript.generateGnssTimeSuggestion(ARBITRARY_TEST_TIME);
mScript.simulateTimePassing()
.simulateGnssTimeSuggestion(timeSuggestion)
.verifySystemClockWasNotSetAndResetCallTracking();
}
@Test
public void gnssTimeSuggestion_ignoredWhenReferencedTimeIsInThePast() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoOriginPriorities(ORIGIN_GNSS)
.pokeAutoTimeDetectionEnabled(true);
Instant suggestedTime = TIME_LOWER_BOUND.minus(Duration.ofDays(1));
GnssTimeSuggestion timeSuggestion = mScript
.generateGnssTimeSuggestion(suggestedTime);
mScript.simulateGnssTimeSuggestion(timeSuggestion)
.verifySystemClockWasNotSetAndResetCallTracking()
.assertLatestGnssSuggestion(null);
}
@Test
public void highPrioritySuggestionsBeatLowerPrioritySuggestions_telephonyNetworkOrigins() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true)
.pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK);
@@ -671,23 +719,131 @@ public class TimeDetectorStrategyImplTest {
mScript.peekBestTelephonySuggestion());
}
@Test
public void highPrioritySuggestionsBeatLowerPrioritySuggestions_networkGnssOrigins() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true)
.pokeAutoOriginPriorities(ORIGIN_NETWORK, ORIGIN_GNSS);
// Three obviously different times that could not be mistaken for each other.
Instant gnssTime1 = ARBITRARY_TEST_TIME;
Instant gnssTime2 = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30));
Instant networkTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(60));
// A small increment used to simulate the passage of time, but not enough to interfere with
// macro-level time changes associated with suggestion age.
final long smallTimeIncrementMillis = 101;
// A gnss suggestion is made. It should be used because there is no network suggestion.
GnssTimeSuggestion gnssTimeSuggestion1 =
mScript.generateGnssTimeSuggestion(gnssTime1);
mScript.simulateTimePassing(smallTimeIncrementMillis)
.simulateGnssTimeSuggestion(gnssTimeSuggestion1)
.verifySystemClockWasSetAndResetCallTracking(
mScript.calculateTimeInMillisForNow(gnssTimeSuggestion1.getUtcTime()));
// Check internal state.
mScript.assertLatestNetworkSuggestion(null)
.assertLatestGnssSuggestion(gnssTimeSuggestion1);
assertEquals(gnssTimeSuggestion1, mScript.peekLatestValidGnssSuggestion());
assertNull("No network suggestions were made:", mScript.peekLatestValidNetworkSuggestion());
// Simulate a little time passing.
mScript.simulateTimePassing(smallTimeIncrementMillis)
.verifySystemClockWasNotSetAndResetCallTracking();
// Now a network suggestion is made. Network suggestions are prioritized over gnss
// suggestions so it should "win".
NetworkTimeSuggestion networkTimeSuggestion =
mScript.generateNetworkTimeSuggestion(networkTime);
mScript.simulateTimePassing(smallTimeIncrementMillis)
.simulateNetworkTimeSuggestion(networkTimeSuggestion)
.verifySystemClockWasSetAndResetCallTracking(
mScript.calculateTimeInMillisForNow(networkTimeSuggestion.getUtcTime()));
// Check internal state.
mScript.assertLatestNetworkSuggestion(networkTimeSuggestion)
.assertLatestGnssSuggestion(gnssTimeSuggestion1);
assertEquals(gnssTimeSuggestion1, mScript.peekLatestValidGnssSuggestion());
assertEquals(networkTimeSuggestion, mScript.peekLatestValidNetworkSuggestion());
// Simulate some significant time passing: half the time allowed before a time signal
// becomes "too old to use".
mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2)
.verifySystemClockWasNotSetAndResetCallTracking();
// Now another gnss suggestion is made. Network suggestions are prioritized over
// gnss suggestions so the latest network suggestion should still "win".
GnssTimeSuggestion gnssTimeSuggestion2 =
mScript.generateGnssTimeSuggestion(gnssTime2);
mScript.simulateTimePassing(smallTimeIncrementMillis)
.simulateGnssTimeSuggestion(gnssTimeSuggestion2)
.verifySystemClockWasNotSetAndResetCallTracking();
// Check internal state.
mScript.assertLatestNetworkSuggestion(networkTimeSuggestion)
.assertLatestGnssSuggestion(gnssTimeSuggestion2);
assertEquals(gnssTimeSuggestion2, mScript.peekLatestValidGnssSuggestion());
assertEquals(networkTimeSuggestion, mScript.peekLatestValidNetworkSuggestion());
// Simulate some significant time passing: half the time allowed before a time signal
// becomes "too old to use". This should mean that telephonyTimeSuggestion is now too old to
// be used but networkTimeSuggestion2 is not.
mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2);
// NOTE: The TimeDetectorStrategyImpl doesn't set an alarm for the point when the last
// suggestion it used becomes too old: it requires a new suggestion or an auto-time toggle
// to re-run the detection logic. This may change in future but until then we rely on a
// steady stream of suggestions to re-evaluate.
mScript.verifySystemClockWasNotSetAndResetCallTracking();
// Check internal state.
mScript.assertLatestNetworkSuggestion(networkTimeSuggestion)
.assertLatestGnssSuggestion(gnssTimeSuggestion2);
assertEquals(gnssTimeSuggestion2, mScript.peekLatestValidGnssSuggestion());
assertNull(
"Network suggestion should be expired:",
mScript.peekLatestValidNetworkSuggestion());
// Toggle auto-time off and on to force the detection logic to run.
mScript.simulateAutoTimeDetectionToggle()
.simulateTimePassing(smallTimeIncrementMillis)
.simulateAutoTimeDetectionToggle();
// Verify the latest gnss time now wins.
mScript.verifySystemClockWasSetAndResetCallTracking(
mScript.calculateTimeInMillisForNow(gnssTimeSuggestion2.getUtcTime()));
// Check internal state.
mScript.assertLatestNetworkSuggestion(networkTimeSuggestion)
.assertLatestGnssSuggestion(gnssTimeSuggestion2);
assertEquals(gnssTimeSuggestion2, mScript.peekLatestValidGnssSuggestion());
assertNull(
"Network suggestion should still be expired:",
mScript.peekLatestValidNetworkSuggestion());
}
@Test
public void whenAllTimeSuggestionsAreAvailable_higherPriorityWins_lowerPriorityComesFirst() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true)
.pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK);
.pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK, ORIGIN_GNSS);
Instant networkTime = ARBITRARY_TEST_TIME;
Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30));
Instant gnssTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30));
Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(60));
NetworkTimeSuggestion networkTimeSuggestion =
mScript.generateNetworkTimeSuggestion(networkTime);
GnssTimeSuggestion gnssTimeSuggestion =
mScript.generateGnssTimeSuggestion(gnssTime);
TelephonyTimeSuggestion telephonyTimeSuggestion =
mScript.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, telephonyTime);
mScript.simulateNetworkTimeSuggestion(networkTimeSuggestion)
.simulateGnssTimeSuggestion(gnssTimeSuggestion)
.simulateTelephonyTimeSuggestion(telephonyTimeSuggestion)
.assertLatestNetworkSuggestion(networkTimeSuggestion)
.assertLatestGnssSuggestion(gnssTimeSuggestion)
.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion)
.verifySystemClockWasSetAndResetCallTracking(telephonyTime.toEpochMilli());
}
@@ -696,20 +852,25 @@ public class TimeDetectorStrategyImplTest {
public void whenAllTimeSuggestionsAreAvailable_higherPriorityWins_higherPriorityComesFirst() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true)
.pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK);
.pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK, ORIGIN_GNSS);
Instant networkTime = ARBITRARY_TEST_TIME;
Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30));
Instant gnssTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(60));
NetworkTimeSuggestion networkTimeSuggestion =
mScript.generateNetworkTimeSuggestion(networkTime);
TelephonyTimeSuggestion telephonyTimeSuggestion =
mScript.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, telephonyTime);
GnssTimeSuggestion gnssTimeSuggestion =
mScript.generateGnssTimeSuggestion(gnssTime);
mScript.simulateTelephonyTimeSuggestion(telephonyTimeSuggestion)
.simulateNetworkTimeSuggestion(networkTimeSuggestion)
.simulateGnssTimeSuggestion(gnssTimeSuggestion)
.assertLatestNetworkSuggestion(networkTimeSuggestion)
.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion)
.assertLatestGnssSuggestion(gnssTimeSuggestion)
.verifySystemClockWasSetAndResetCallTracking(telephonyTime.toEpochMilli());
}
@@ -728,7 +889,37 @@ public class TimeDetectorStrategyImplTest {
}
@Test
public void suggestionsFromSourceNotListedInPrioritiesList_areIgnored() {
public void whenHigherPrioritySuggestionsAreNotAvailable_fallbacksToNext() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true)
.pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK, ORIGIN_GNSS);
GnssTimeSuggestion timeSuggestion =
mScript.generateGnssTimeSuggestion(ARBITRARY_TEST_TIME);
mScript.simulateGnssTimeSuggestion(timeSuggestion)
.assertLatestGnssSuggestion(timeSuggestion)
.verifySystemClockWasSetAndResetCallTracking(ARBITRARY_TEST_TIME.toEpochMilli());
}
@Test
public void suggestionsFromTelephonyOriginNotInPriorityList_areIgnored() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true)
.pokeAutoOriginPriorities(ORIGIN_NETWORK);
int slotIndex = ARBITRARY_SLOT_INDEX;
Instant testTime = ARBITRARY_TEST_TIME;
TelephonyTimeSuggestion timeSuggestion =
mScript.generateTelephonyTimeSuggestion(slotIndex, testTime);
mScript.simulateTelephonyTimeSuggestion(timeSuggestion)
.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, timeSuggestion)
.verifySystemClockWasNotSetAndResetCallTracking();
}
@Test
public void suggestionsFromNetworkOriginNotInPriorityList_areIgnored() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true)
.pokeAutoOriginPriorities(ORIGIN_TELEPHONY);
@@ -741,6 +932,20 @@ public class TimeDetectorStrategyImplTest {
.verifySystemClockWasNotSetAndResetCallTracking();
}
@Test
public void suggestionsFromGnssOriginNotInPriorityList_areIgnored() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true)
.pokeAutoOriginPriorities(ORIGIN_TELEPHONY);
GnssTimeSuggestion timeSuggestion = mScript.generateGnssTimeSuggestion(
ARBITRARY_TEST_TIME);
mScript.simulateGnssTimeSuggestion(timeSuggestion)
.assertLatestGnssSuggestion(timeSuggestion)
.verifySystemClockWasNotSetAndResetCallTracking();
}
@Test
public void autoOriginPrioritiesList_doesNotAffectManualSuggestion() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
@@ -945,6 +1150,11 @@ public class TimeDetectorStrategyImplTest {
return this;
}
Script simulateGnssTimeSuggestion(GnssTimeSuggestion timeSuggestion) {
mTimeDetectorStrategy.suggestGnssTime(timeSuggestion);
return this;
}
Script simulateAutoTimeDetectionToggle() {
mFakeCallback.simulateAutoTimeZoneDetectionToggle();
mTimeDetectorStrategy.handleAutoTimeConfigChanged();
@@ -994,6 +1204,14 @@ public class TimeDetectorStrategyImplTest {
return this;
}
/**
* White box test info: Asserts the latest gnss suggestion is as expected.
*/
Script assertLatestGnssSuggestion(GnssTimeSuggestion expected) {
assertEquals(expected, mTimeDetectorStrategy.getLatestGnssSuggestion());
return this;
}
/**
* White box test info: Returns the telephony suggestion that would be used, if any, given
* the current elapsed real time clock and regardless of origin prioritization.
@@ -1010,6 +1228,14 @@ public class TimeDetectorStrategyImplTest {
return mTimeDetectorStrategy.findLatestValidNetworkSuggestionForTests();
}
/**
* White box test info: Returns the gnss suggestion that would be used, if any, given the
* current elapsed real time clock and regardless of origin prioritization.
*/
GnssTimeSuggestion peekLatestValidGnssSuggestion() {
return mTimeDetectorStrategy.findLatestValidGnssSuggestionForTests();
}
/**
* Generates a ManualTimeSuggestion using the current elapsed realtime clock for the
* reference time.
@@ -1056,6 +1282,18 @@ public class TimeDetectorStrategyImplTest {
return new NetworkTimeSuggestion(utcTime);
}
/**
* Generates a GnssTimeSuggestion using the current elapsed realtime clock for the
* reference time.
*/
GnssTimeSuggestion generateGnssTimeSuggestion(Instant suggestedTime) {
TimestampedValue<Long> utcTime =
new TimestampedValue<>(
mFakeCallback.peekElapsedRealtimeMillis(),
suggestedTime.toEpochMilli());
return new GnssTimeSuggestion(utcTime);
}
/**
* Calculates what the supplied time would be when adjusted for the movement of the fake
* elapsed realtime clock.