diff options
author | 2023-02-14 15:30:57 +0000 | |
---|---|---|
committer | 2023-02-14 15:30:57 +0000 | |
commit | 1b3159f48e9ec130f3810120f14c94bfa7d67592 (patch) | |
tree | 0af817cdc4bcd77b5736f79ea154b81a036e1fc6 /services/robotests/src | |
parent | c55eb34f877af1b91dbf8b232221908b7c2b7a6e (diff) | |
parent | bd64b8d54323772d050270420e33acc424e6c805 (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.java | 399 |
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); + } + } +} |