summaryrefslogtreecommitdiff
path: root/services/robotests/src
diff options
context:
space:
mode:
author Neil Fuller <nfuller@google.com> 2023-02-14 15:30:57 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-02-14 15:30:57 +0000
commit1b3159f48e9ec130f3810120f14c94bfa7d67592 (patch)
tree0af817cdc4bcd77b5736f79ea154b81a036e1fc6 /services/robotests/src
parentc55eb34f877af1b91dbf8b232221908b7c2b7a6e (diff)
parentbd64b8d54323772d050270420e33acc424e6c805 (diff)
Merge "Add a new NetworkTimeHelper impl"
Diffstat (limited to 'services/robotests/src')
-rw-r--r--services/robotests/src/com/android/server/location/gnss/TimeDetectorNetworkTimeHelperTest.java399
1 files changed, 399 insertions, 0 deletions
diff --git a/services/robotests/src/com/android/server/location/gnss/TimeDetectorNetworkTimeHelperTest.java b/services/robotests/src/com/android/server/location/gnss/TimeDetectorNetworkTimeHelperTest.java
new file mode 100644
index 000000000000..3e2e46c520c0
--- /dev/null
+++ b/services/robotests/src/com/android/server/location/gnss/TimeDetectorNetworkTimeHelperTest.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.location.gnss;
+
+import static com.android.server.location.gnss.TimeDetectorNetworkTimeHelper.MAX_NETWORK_TIME_AGE_MILLIS;
+import static com.android.server.location.gnss.TimeDetectorNetworkTimeHelper.NTP_REFRESH_INTERVAL_MILLIS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.app.time.UnixEpochTime;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.location.gnss.NetworkTimeHelper.InjectTimeCallback;
+import com.android.server.location.gnss.TimeDetectorNetworkTimeHelper.Environment;
+import com.android.server.timedetector.NetworkTimeSuggestion;
+import com.android.server.timezonedetector.StateChangeListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+/**
+ * Unit tests for {@link TimeDetectorNetworkTimeHelper}.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class TimeDetectorNetworkTimeHelperTest {
+
+ private static final NetworkTimeSuggestion ARBITRARY_NETWORK_TIME =
+ new NetworkTimeSuggestion(new UnixEpochTime(1234L, 7777L), 123);
+
+ private FakeEnvironment mFakeEnvironment;
+ @Mock private InjectTimeCallback mMockInjectTimeCallback;
+ private TimeDetectorNetworkTimeHelper mTimeDetectorNetworkTimeHelper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mFakeEnvironment = new FakeEnvironment();
+ mTimeDetectorNetworkTimeHelper = new TimeDetectorNetworkTimeHelper(
+ mFakeEnvironment, mMockInjectTimeCallback);
+
+ // TimeDetectorNetworkTimeHelper should register for network time updates during
+ // construction.
+ mFakeEnvironment.assertHasNetworkTimeChangeListener();
+ }
+
+ @Test
+ public void setPeriodicTimeInjectionMode_true() {
+ testSetPeriodicTimeInjectionMode(true);
+ }
+
+ @Test
+ public void setPeriodicTimeInjectionMode_false() {
+ testSetPeriodicTimeInjectionMode(false);
+ }
+
+ private void testSetPeriodicTimeInjectionMode(boolean periodicTimeInjectionMode) {
+ NetworkTimeSuggestion networkTime = ARBITRARY_NETWORK_TIME;
+ int millisElapsedSinceNetworkTimeReceived = 1000;
+ mFakeEnvironment.pokeLatestNetworkTime(networkTime);
+
+ long currentElapsedRealtimeMillis =
+ networkTime.getUnixEpochTime().getElapsedRealtimeMillis()
+ + millisElapsedSinceNetworkTimeReceived;
+ mFakeEnvironment.pokeElapsedRealtimeMillis(currentElapsedRealtimeMillis);
+
+ mTimeDetectorNetworkTimeHelper.setPeriodicTimeInjectionMode(periodicTimeInjectionMode);
+
+ // All injections are async, so we have to simulate the async work taking place.
+ mFakeEnvironment.assertHasNoScheduledAsyncCallback();
+ mFakeEnvironment.assertHasImmediateCallback();
+ mFakeEnvironment.simulateTimeAdvancing(1);
+
+ // Any call to setPeriodicTimeInjectionMode() should result in an (async) injected time
+ verify(mMockInjectTimeCallback).injectTime(
+ networkTime.getUnixEpochTime().getUnixEpochTimeMillis(),
+ networkTime.getUnixEpochTime().getElapsedRealtimeMillis(),
+ networkTime.getUncertaintyMillis());
+
+ // Check whether the scheduled async is set up / not set up for the periodic request.
+ if (periodicTimeInjectionMode) {
+ mFakeEnvironment.assertHasScheduledAsyncCallback(
+ mFakeEnvironment.elapsedRealtimeMillis() + NTP_REFRESH_INTERVAL_MILLIS);
+ } else {
+ mFakeEnvironment.assertHasNoScheduledAsyncCallback();
+ }
+ }
+
+ @Test
+ public void periodicInjectionBehavior() {
+ // Set the elapsed realtime clock to an arbitrary start value.
+ mFakeEnvironment.pokeElapsedRealtimeMillis(12345L);
+
+ // Configure periodic time injections. Doing so should cause a time query, but no time is
+ // available.
+ mTimeDetectorNetworkTimeHelper.setPeriodicTimeInjectionMode(true);
+
+ // All query/injections are async, so we have to simulate the async work taking place.
+ mFakeEnvironment.assertHasImmediateCallback();
+ mFakeEnvironment.simulateTimeAdvancing(1);
+
+ // No time available, so no injection.
+ verifyNoMoreInteractions(mMockInjectTimeCallback);
+
+ // A periodic check should be scheduled.
+ mFakeEnvironment.assertHasScheduledAsyncCallback(
+ mFakeEnvironment.elapsedRealtimeMillis() + NTP_REFRESH_INTERVAL_MILLIS);
+
+ // Time passes...
+ mFakeEnvironment.simulateTimeAdvancing(NTP_REFRESH_INTERVAL_MILLIS / 2);
+
+ // A network time becomes available: This should cause the registered listener to trigger.
+ NetworkTimeSuggestion networkTime = ARBITRARY_NETWORK_TIME;
+ mFakeEnvironment.simulateLatestNetworkTimeChange(networkTime);
+
+ // All query/injections are async, so we have to simulate the async work taking place,
+ // causing a query, time injection and a re-schedule.
+ mFakeEnvironment.simulateTimeAdvancing(1);
+ verify(mMockInjectTimeCallback).injectTime(
+ networkTime.getUnixEpochTime().getUnixEpochTimeMillis(),
+ networkTime.getUnixEpochTime().getElapsedRealtimeMillis(),
+ networkTime.getUncertaintyMillis());
+
+ // A new periodic check should be scheduled.
+ mFakeEnvironment.assertHasNoImmediateCallback();
+
+ mFakeEnvironment.assertHasScheduledAsyncCallback(
+ mFakeEnvironment.elapsedRealtimeMillis() + NTP_REFRESH_INTERVAL_MILLIS);
+
+ int arbitraryIterationCount = 3;
+ for (int i = 0; i < arbitraryIterationCount; i++) {
+ // Advance by the amount needed for the scheduled work to run. That work should query
+ // and inject.
+ mFakeEnvironment.simulateTimeAdvancing(NTP_REFRESH_INTERVAL_MILLIS);
+
+ // All query/injections are async, so we have to simulate the async work taking place,
+ // causing a query, time injection and a re-schedule.
+ verify(mMockInjectTimeCallback).injectTime(
+ networkTime.getUnixEpochTime().getUnixEpochTimeMillis(),
+ networkTime.getUnixEpochTime().getElapsedRealtimeMillis(),
+ networkTime.getUncertaintyMillis());
+
+ // A new periodic check should be scheduled.
+ mFakeEnvironment.assertHasScheduledAsyncCallback(
+ mFakeEnvironment.elapsedRealtimeMillis() + NTP_REFRESH_INTERVAL_MILLIS);
+ mFakeEnvironment.assertHasNoImmediateCallback();
+ }
+ }
+
+ @Test
+ public void networkTimeAvailableBehavior() {
+ // Set the elapsed realtime clock to an arbitrary start value.
+ mFakeEnvironment.pokeElapsedRealtimeMillis(12345L);
+
+ // No periodic time injections. This call causes a time query, but no time is available yet.
+ mTimeDetectorNetworkTimeHelper.setPeriodicTimeInjectionMode(false);
+
+ // All query/injections are async, so we have to simulate the async work taking place.
+ mFakeEnvironment.assertHasNoScheduledAsyncCallback();
+ mFakeEnvironment.assertHasImmediateCallback();
+ mFakeEnvironment.simulateTimeAdvancing(1);
+
+ // No time available, so no injection.
+ verifyNoMoreInteractions(mMockInjectTimeCallback);
+
+ // No periodic check should be scheduled.
+ mFakeEnvironment.assertHasNoScheduledAsyncCallback();
+
+ // Time passes...
+ mFakeEnvironment.simulateTimeAdvancing(NTP_REFRESH_INTERVAL_MILLIS / 2);
+
+ // A network time becomes available: This should cause the registered listener to trigger
+ // and cause time to be injected.
+ NetworkTimeSuggestion networkTime = ARBITRARY_NETWORK_TIME;
+ mFakeEnvironment.simulateLatestNetworkTimeChange(networkTime);
+
+ // All query/injections are async, so we have to simulate the async work taking place,
+ // causing a query, time injection and a re-schedule.
+ mFakeEnvironment.assertHasNoScheduledAsyncCallback();
+ mFakeEnvironment.assertHasImmediateCallback();
+ mFakeEnvironment.simulateTimeAdvancing(1);
+ verify(mMockInjectTimeCallback).injectTime(
+ networkTime.getUnixEpochTime().getUnixEpochTimeMillis(),
+ networkTime.getUnixEpochTime().getElapsedRealtimeMillis(),
+ networkTime.getUncertaintyMillis());
+
+ // No periodic check should be scheduled.
+ mFakeEnvironment.assertHasNoScheduledAsyncCallback();
+ mFakeEnvironment.assertHasNoImmediateCallback();
+ }
+
+ @Test
+ public void networkConnectivityAvailableBehavior() {
+ // Set the elapsed realtime clock to an arbitrary start value.
+ mFakeEnvironment.pokeElapsedRealtimeMillis(12345L);
+
+ // No periodic time injections. This call causes a time query, but no time is available yet.
+ mTimeDetectorNetworkTimeHelper.setPeriodicTimeInjectionMode(false);
+
+ // All query/injections are async, so we have to simulate the async work taking place.
+ mFakeEnvironment.assertHasNoScheduledAsyncCallback();
+ mFakeEnvironment.assertHasImmediateCallback();
+ mFakeEnvironment.simulateTimeAdvancing(1);
+
+ // No time available, so no injection.
+ verifyNoMoreInteractions(mMockInjectTimeCallback);
+
+ // No periodic check should be scheduled.
+ mFakeEnvironment.assertHasNoScheduledAsyncCallback();
+
+ // Time passes...
+ mFakeEnvironment.simulateTimeAdvancing(NTP_REFRESH_INTERVAL_MILLIS / 2);
+
+ NetworkTimeSuggestion networkTime = ARBITRARY_NETWORK_TIME;
+ mFakeEnvironment.pokeLatestNetworkTime(networkTime);
+
+ // Simulate location code noticing that connectivity has changed and notifying the helper.
+ mTimeDetectorNetworkTimeHelper.onNetworkAvailable();
+
+ // All query/injections are async, so we have to simulate the async work taking place,
+ // causing a query, time injection and a re-schedule.
+ mFakeEnvironment.assertHasNoScheduledAsyncCallback();
+ mFakeEnvironment.assertHasImmediateCallback();
+ mFakeEnvironment.simulateTimeAdvancing(1);
+ verify(mMockInjectTimeCallback).injectTime(
+ networkTime.getUnixEpochTime().getUnixEpochTimeMillis(),
+ networkTime.getUnixEpochTime().getElapsedRealtimeMillis(),
+ networkTime.getUncertaintyMillis());
+
+ // No periodic check should be scheduled.
+ mFakeEnvironment.assertHasNoScheduledAsyncCallback();
+ mFakeEnvironment.assertHasNoImmediateCallback();
+ }
+
+ @Test
+ public void oldTimesNotInjected() {
+ NetworkTimeSuggestion networkTime = ARBITRARY_NETWORK_TIME;
+ mFakeEnvironment.pokeLatestNetworkTime(networkTime);
+
+ int millisElapsedSinceNetworkTimeReceived = MAX_NETWORK_TIME_AGE_MILLIS;
+ long currentElapsedRealtimeMillis =
+ networkTime.getUnixEpochTime().getElapsedRealtimeMillis()
+ + millisElapsedSinceNetworkTimeReceived;
+ mFakeEnvironment.pokeElapsedRealtimeMillis(currentElapsedRealtimeMillis);
+
+ mTimeDetectorNetworkTimeHelper.setPeriodicTimeInjectionMode(true);
+
+ // All injections are async, so we have to simulate the async work taking place.
+ mFakeEnvironment.assertHasNoScheduledAsyncCallback();
+ mFakeEnvironment.assertHasImmediateCallback();
+
+ // The age of the network time will now be MAX_NETWORK_TIME_AGE_MILLIS + 1, which is too
+ // old to inject.
+ mFakeEnvironment.simulateTimeAdvancing(1);
+
+ // Old network times should not be injected.
+ verify(mMockInjectTimeCallback, never()).injectTime(anyLong(), anyLong(), anyInt());
+
+ // Check whether the scheduled async is set up / not set up for the periodic request.
+ mFakeEnvironment.assertHasScheduledAsyncCallback(
+ mFakeEnvironment.elapsedRealtimeMillis() + NTP_REFRESH_INTERVAL_MILLIS);
+ }
+
+ /** A fake implementation of {@link Environment} for use by this test. */
+ private static class FakeEnvironment implements Environment {
+
+ private StateChangeListener mNetworkTimeUpdateListener;
+
+ private long mCurrentElapsedRealtimeMillis;
+ private NetworkTimeSuggestion mLatestNetworkTime;
+
+ private TimeDetectorNetworkTimeHelper mImmediateAsyncCallback;
+ private String mImmediateAsyncCallbackReason;
+
+ private TimeDetectorNetworkTimeHelper mScheduledAsyncCallback;
+ private long mScheduledAsyncRunnableTimeMillis;
+
+ @Override
+ public long elapsedRealtimeMillis() {
+ return mCurrentElapsedRealtimeMillis;
+ }
+
+ @Override
+ public NetworkTimeSuggestion getLatestNetworkTime() {
+ return mLatestNetworkTime;
+ }
+
+ @Override
+ public void setNetworkTimeUpdateListener(StateChangeListener stateChangeListener) {
+ mNetworkTimeUpdateListener = stateChangeListener;
+ }
+
+ @Override
+ public void requestImmediateTimeQueryCallback(TimeDetectorNetworkTimeHelper helper,
+ String reason) {
+ if (mImmediateAsyncCallback != null) {
+ fail("Only one immediate callback expected at a time, found reason: "
+ + mImmediateAsyncCallbackReason);
+ }
+ mImmediateAsyncCallback = helper;
+ mImmediateAsyncCallbackReason = reason;
+ }
+
+ @Override
+ public void requestDelayedTimeQueryCallback(
+ TimeDetectorNetworkTimeHelper instance, long delayMillis) {
+ mScheduledAsyncCallback = instance;
+ mScheduledAsyncRunnableTimeMillis = mCurrentElapsedRealtimeMillis + delayMillis;
+ }
+
+ @Override
+ public void clearDelayedTimeQueryCallback() {
+ mScheduledAsyncCallback = null;
+ mScheduledAsyncRunnableTimeMillis = -1;
+ }
+
+ void pokeLatestNetworkTime(NetworkTimeSuggestion networkTime) {
+ mLatestNetworkTime = networkTime;
+ }
+
+ void pokeElapsedRealtimeMillis(long currentElapsedRealtimeMillis) {
+ mCurrentElapsedRealtimeMillis = currentElapsedRealtimeMillis;
+ }
+
+ void simulateLatestNetworkTimeChange(NetworkTimeSuggestion networkTime) {
+ mLatestNetworkTime = networkTime;
+ mNetworkTimeUpdateListener.onChange();
+ }
+
+ void simulateTimeAdvancing(long durationMillis) {
+ mCurrentElapsedRealtimeMillis += durationMillis;
+
+ if (mImmediateAsyncCallback != null) {
+ TimeDetectorNetworkTimeHelper helper = mImmediateAsyncCallback;
+ String reason = mImmediateAsyncCallbackReason;
+ mImmediateAsyncCallback = null;
+ mImmediateAsyncCallbackReason = null;
+ helper.queryAndInjectNetworkTime(reason);
+ }
+
+ if (mScheduledAsyncCallback != null
+ && mCurrentElapsedRealtimeMillis >= mScheduledAsyncRunnableTimeMillis) {
+ TimeDetectorNetworkTimeHelper helper = mScheduledAsyncCallback;
+ mScheduledAsyncCallback = null;
+ mScheduledAsyncRunnableTimeMillis = -1;
+ helper.delayedQueryAndInjectNetworkTime();
+ }
+ }
+
+ void assertHasNetworkTimeChangeListener() {
+ assertNotNull(mNetworkTimeUpdateListener);
+ }
+
+ void assertHasImmediateCallback() {
+ assertNotNull(mImmediateAsyncCallback);
+ }
+
+ void assertHasNoImmediateCallback() {
+ assertNull(mImmediateAsyncCallback);
+ }
+
+ void assertHasScheduledAsyncCallback(long expectedScheduledAsyncRunnableTimeMillis) {
+ assertNotNull(mScheduledAsyncCallback);
+ assertEquals(expectedScheduledAsyncRunnableTimeMillis,
+ mScheduledAsyncRunnableTimeMillis);
+ }
+
+ void assertHasNoScheduledAsyncCallback() {
+ assertNull(mScheduledAsyncCallback);
+ }
+ }
+}