From 20d3b3c4c350e1100139f8ed238cbb94eb9bb5dc Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Tue, 20 Dec 2022 13:47:58 +0000 Subject: [PATCH] Add time detector network behavior Add methods to enable the latest network time signal to be retrieved and cleared. Also add shell command support for their use in tests. getLatestNetworkSuggestion() already existed for internal use and has now been exposed. clearNetworkTime() / clearLatestNetworkSuggestion() have been added for use in a future commit. Bug: 222295093 Test: build / boot / treehugger Test: atest services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java Change-Id: I9ffc00b6f2f5a12cbe3e20fc5be544fd2aedee37 --- core/java/android/app/time/UnixEpochTime.java | 2 +- .../app/timedetector/TimeDetector.java | 12 +++ .../timedetector/TimeDetectorService.java | 34 ++++++++- .../TimeDetectorShellCommand.java | 22 ++++++ .../timedetector/TimeDetectorStrategy.java | 15 ++++ .../TimeDetectorStrategyImpl.java | 24 +++--- .../FakeTimeDetectorStrategy.java | 9 +++ .../timedetector/TimeDetectorServiceTest.java | 22 ++++++ .../TimeDetectorStrategyImplTest.java | 73 +++++++++++++++++-- 9 files changed, 193 insertions(+), 20 deletions(-) diff --git a/core/java/android/app/time/UnixEpochTime.java b/core/java/android/app/time/UnixEpochTime.java index 3a35f3cd1acb3..4ab81abfe9afc 100644 --- a/core/java/android/app/time/UnixEpochTime.java +++ b/core/java/android/app/time/UnixEpochTime.java @@ -121,7 +121,7 @@ public final class UnixEpochTime implements Parcelable { @Override public String toString() { return "UnixEpochTime{" - + "mElapsedRealtimeTimeMillis=" + mElapsedRealtimeMillis + + "mElapsedRealtimeMillis=" + mElapsedRealtimeMillis + ", mUnixEpochTimeMillis=" + mUnixEpochTimeMillis + '}'; } diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java index f95d6d3bb0563..50a7da1cede57 100644 --- a/core/java/android/app/timedetector/TimeDetector.java +++ b/core/java/android/app/timedetector/TimeDetector.java @@ -72,6 +72,18 @@ public interface TimeDetector { */ String SHELL_COMMAND_SUGGEST_NETWORK_TIME = "suggest_network_time"; + /** + * A shell command that prints the current network time information. + * @hide + */ + String SHELL_COMMAND_GET_NETWORK_TIME = "get_network_time"; + + /** + * A shell command that clears the detector's network time information. + * @hide + */ + String SHELL_COMMAND_CLEAR_NETWORK_TIME = "clear_network_time"; + /** * A shell command that injects a GNSS time suggestion. * @hide diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java index 1be9074e079a9..3e2395303354a 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java @@ -143,7 +143,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub return getTimeCapabilitiesAndConfig(userId); } - TimeCapabilitiesAndConfig getTimeCapabilitiesAndConfig(@UserIdInt int userId) { + private TimeCapabilitiesAndConfig getTimeCapabilitiesAndConfig(@UserIdInt int userId) { enforceManageTimeDetectorPermission(); final long token = mCallerIdentityInjector.clearCallingIdentity(); @@ -163,6 +163,9 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub return updateConfiguration(callingUserId, configuration); } + /** + * Updates the user's configuration. Exposed for use by {@link TimeDetectorShellCommand}. + */ boolean updateConfiguration(@UserIdInt int userId, @NonNull TimeConfiguration configuration) { // Resolve constants like USER_CURRENT to the true user ID as needed. int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), @@ -256,7 +259,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub } } - void handleConfigurationInternalChangedOnHandlerThread() { + private void handleConfigurationInternalChangedOnHandlerThread() { // Configuration has changed, but each user may have a different view of the configuration. // It's possible that this will cause unnecessary notifications but that shouldn't be a // problem. @@ -287,6 +290,10 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub } } + /** + * Sets the system time state. See {@link TimeState} for details. For use by {@link + * TimeDetectorShellCommand}. + */ void setTimeState(@NonNull TimeState timeState) { enforceManageTimeDetectorPermission(); @@ -353,6 +360,9 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub } } + /** + * Suggests network time with permission checks. For use by {@link TimeDetectorShellCommand}. + */ void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal) { enforceSuggestNetworkTimePermission(); Objects.requireNonNull(timeSignal); @@ -360,6 +370,23 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal)); } + /** + * Clears the cached network time information. For use during tests to simulate when no network + * time has been made available. For use by {@link TimeDetectorShellCommand}. + * + *

This operation takes place in the calling thread. + */ + void clearNetworkTime() { + enforceSuggestNetworkTimePermission(); + + final long token = Binder.clearCallingIdentity(); + try { + mTimeDetectorStrategy.clearLatestNetworkSuggestion(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + @Override public UnixEpochTime latestNetworkTime() { NetworkTimeSuggestion suggestion = getLatestNetworkSuggestion(); @@ -388,6 +415,9 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub } } + /** + * Suggests GNSS time with permission checks. For use by {@link TimeDetectorShellCommand}. + */ void suggestGnssTime(@NonNull GnssTimeSuggestion timeSignal) { enforceSuggestGnssTimePermission(); Objects.requireNonNull(timeSignal); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java b/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java index 990c00feae16b..cce5709861689 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java @@ -15,7 +15,9 @@ */ package com.android.server.timedetector; +import static android.app.timedetector.TimeDetector.SHELL_COMMAND_CLEAR_NETWORK_TIME; import static android.app.timedetector.TimeDetector.SHELL_COMMAND_CONFIRM_TIME; +import static android.app.timedetector.TimeDetector.SHELL_COMMAND_GET_NETWORK_TIME; import static android.app.timedetector.TimeDetector.SHELL_COMMAND_GET_TIME_STATE; import static android.app.timedetector.TimeDetector.SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED; import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SERVICE_NAME; @@ -70,6 +72,10 @@ class TimeDetectorShellCommand extends ShellCommand { return runSuggestTelephonyTime(); case SHELL_COMMAND_SUGGEST_NETWORK_TIME: return runSuggestNetworkTime(); + case SHELL_COMMAND_GET_NETWORK_TIME: + return runGetNetworkTime(); + case SHELL_COMMAND_CLEAR_NETWORK_TIME: + return runClearNetworkTime(); case SHELL_COMMAND_SUGGEST_GNSS_TIME: return runSuggestGnssTime(); case SHELL_COMMAND_SUGGEST_EXTERNAL_TIME: @@ -122,6 +128,18 @@ class TimeDetectorShellCommand extends ShellCommand { mInterface::suggestNetworkTime); } + private int runGetNetworkTime() { + NetworkTimeSuggestion networkTimeSuggestion = mInterface.getLatestNetworkSuggestion(); + final PrintWriter pw = getOutPrintWriter(); + pw.println(networkTimeSuggestion); + return 0; + } + + private int runClearNetworkTime() { + mInterface.clearNetworkTime(); + return 0; + } + private int runSuggestGnssTime() { return runSuggestTime( () -> GnssTimeSuggestion.parseCommandLineArg(this), @@ -196,6 +214,10 @@ class TimeDetectorShellCommand extends ShellCommand { pw.printf(" Sets the current time state for tests.\n"); pw.printf(" %s \n", SHELL_COMMAND_CONFIRM_TIME); pw.printf(" Tries to confirms the time, raising the confidence.\n"); + pw.printf(" %s\n", SHELL_COMMAND_GET_NETWORK_TIME); + pw.printf(" Prints the network time information held by the detector.\n"); + pw.printf(" %s\n", SHELL_COMMAND_CLEAR_NETWORK_TIME); + pw.printf(" Clears the network time information held by the detector.\n"); pw.println(); ManualTimeSuggestion.printCommandLineOpts(pw); pw.println(); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java index 03f236d9b30d2..9dca6ec26d298 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java @@ -18,6 +18,7 @@ package com.android.server.timedetector; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.time.ExternalTimeSuggestion; import android.app.time.TimeState; @@ -103,6 +104,20 @@ public interface TimeDetectorStrategy extends Dumpable { /** Processes the suggested time from network sources. */ void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion); + /** + * Returns the latest (accepted) network time suggestion. Returns {@code null} if there isn't + * one. + */ + @Nullable + NetworkTimeSuggestion getLatestNetworkSuggestion(); + + /** + * Clears the latest network time suggestion, leaving none. The remaining time signals from + * other sources will be reassessed causing the device's time to be updated if config and + * settings allow. + */ + void clearLatestNetworkSuggestion(); + /** Processes the suggested time from gnss sources. */ void suggestGnssTime(@NonNull GnssTimeSuggestion timeSuggestion); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index 13ec75329e39a..09bb8036406d8 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -315,6 +315,21 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { doAutoTimeDetection(reason); } + @Override + @Nullable + public synchronized NetworkTimeSuggestion getLatestNetworkSuggestion() { + return mLastNetworkSuggestion.get(); + } + + @Override + public synchronized void clearLatestNetworkSuggestion() { + mLastNetworkSuggestion.set(null); + + // The loss of network time may change the time signal to use to set the system clock. + String reason = "Network time cleared"; + doAutoTimeDetection(reason); + } + @Override @NonNull public synchronized TimeState getTimeState() { @@ -1063,15 +1078,6 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { return mSuggestionBySlotIndex.get(slotIndex); } - /** - * A method used to inspect state during tests. Not intended for general use. - */ - @VisibleForTesting - @Nullable - public synchronized NetworkTimeSuggestion getLatestNetworkSuggestion() { - return mLastNetworkSuggestion.get(); - } - /** * A method used to inspect state during tests. Not intended for general use. */ diff --git a/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java index 856df359b3269..50040b70c47ee 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java @@ -61,6 +61,15 @@ public class FakeTimeDetectorStrategy implements TimeDetectorStrategy { public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) { } + @Override + public NetworkTimeSuggestion getLatestNetworkSuggestion() { + return null; + } + + @Override + public void clearLatestNetworkSuggestion() { + } + @Override public void suggestGnssTime(GnssTimeSuggestion timeSuggestion) { } diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index a8572381208d0..0b339ad52eda3 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -379,6 +379,28 @@ public class TimeDetectorServiceTest { verify(mFakeTimeDetectorStrategySpy).suggestExternalTime(externalTimeSuggestion); } + @Test + public void testClearNetworkTime_withoutPermission() { + doThrow(new SecurityException("Mock")) + .when(mMockContext).enforceCallingPermission(anyString(), any()); + + assertThrows(SecurityException.class, + () -> mTimeDetectorService.clearNetworkTime()); + verify(mMockContext).enforceCallingPermission( + eq(android.Manifest.permission.SET_TIME), anyString()); + } + + @Test + public void testClearNetworkTime() throws Exception { + doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); + + mTimeDetectorService.clearNetworkTime(); + + verify(mMockContext).enforceCallingPermission( + eq(android.Manifest.permission.SET_TIME), anyString()); + verify(mFakeTimeDetectorStrategySpy).clearLatestNetworkSuggestion(); + } + @Test public void testLatestNetworkTime() { NtpTrustedTime.TimeResult latestNetworkTime = new NtpTrustedTime.TimeResult( diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index caef4943118b2..37da2a28a892f 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -874,6 +874,7 @@ public class TimeDetectorStrategyImplTest { long expectedSystemClockMillis = script.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); script.simulateNetworkTimeSuggestion(timeSuggestion) + .assertLatestNetworkSuggestion(timeSuggestion) .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); } @@ -891,9 +892,54 @@ public class TimeDetectorStrategyImplTest { script.simulateTimePassing() .simulateNetworkTimeSuggestion(timeSuggestion) + .assertLatestNetworkSuggestion(timeSuggestion) .verifySystemClockWasNotSetAndResetCallTracking(); } + @Test + public void testClearLatestNetworkSuggestion() { + ConfigurationInternal configInternal = + new ConfigurationInternal.Builder(CONFIG_AUTO_ENABLED) + .setOriginPriorities(ORIGIN_NETWORK, ORIGIN_EXTERNAL) + .build(); + Script script = new Script().simulateConfigurationInternalChange(configInternal); + + // Create two different time suggestions for the current elapsedRealtimeMillis. + ExternalTimeSuggestion externalTimeSuggestion = + script.generateExternalTimeSuggestion(ARBITRARY_TEST_TIME); + NetworkTimeSuggestion networkTimeSuggestion = + script.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME.plus(Duration.ofHours(5))); + script.simulateTimePassing(); + + // Suggest an external time: This should cause the device to change time. + { + long expectedSystemClockMillis = + script.calculateTimeInMillisForNow(externalTimeSuggestion.getUnixEpochTime()); + script.simulateExternalTimeSuggestion(externalTimeSuggestion) + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); + } + + // Suggest a network time: This should cause the device to change time because + // network > external. + { + long expectedSystemClockMillis = + script.calculateTimeInMillisForNow(networkTimeSuggestion.getUnixEpochTime()); + script.simulateNetworkTimeSuggestion(networkTimeSuggestion) + .assertLatestNetworkSuggestion(networkTimeSuggestion) + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); + } + + // Clear the network time. This should cause the device to change back to the external time, + // which is now the best time available. + { + long expectedSystemClockMillis = + script.calculateTimeInMillisForNow(externalTimeSuggestion.getUnixEpochTime()); + script.simulateClearLatestNetworkSuggestion() + .assertLatestNetworkSuggestion(null) + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); + } + } + @Test public void testSuggestNetworkTime_rejectedBelowLowerBound() { ConfigurationInternal configInternal = @@ -908,6 +954,7 @@ public class TimeDetectorStrategyImplTest { NetworkTimeSuggestion timeSuggestion = script.generateNetworkTimeSuggestion(belowLowerBound); script.simulateNetworkTimeSuggestion(timeSuggestion) + .assertLatestNetworkSuggestion(null) .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -926,6 +973,7 @@ public class TimeDetectorStrategyImplTest { NetworkTimeSuggestion timeSuggestion = script.generateNetworkTimeSuggestion(aboveLowerBound); script.simulateNetworkTimeSuggestion(timeSuggestion) + .assertLatestNetworkSuggestion(timeSuggestion) .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(aboveLowerBound.toEpochMilli()); } @@ -944,6 +992,7 @@ public class TimeDetectorStrategyImplTest { NetworkTimeSuggestion timeSuggestion = script.generateNetworkTimeSuggestion(aboveUpperBound); script.simulateNetworkTimeSuggestion(timeSuggestion) + .assertLatestNetworkSuggestion(null) .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -962,6 +1011,7 @@ public class TimeDetectorStrategyImplTest { NetworkTimeSuggestion timeSuggestion = script.generateNetworkTimeSuggestion(belowUpperBound); script.simulateNetworkTimeSuggestion(timeSuggestion) + .assertLatestNetworkSuggestion(timeSuggestion) .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(belowUpperBound.toEpochMilli()); } @@ -1745,7 +1795,7 @@ public class TimeDetectorStrategyImplTest { } @Test - public void suggestionsFromNetworkOriginNotInPriorityList_areIgnored() { + public void suggestionsFromNetworkOriginNotInPriorityList_areNotUsed() { ConfigurationInternal configInternal = new ConfigurationInternal.Builder(CONFIG_AUTO_ENABLED) .setOriginPriorities(ORIGIN_TELEPHONY) @@ -1756,12 +1806,13 @@ public class TimeDetectorStrategyImplTest { ARBITRARY_TEST_TIME); script.simulateNetworkTimeSuggestion(timeSuggestion) + .assertLatestNetworkSuggestion(timeSuggestion) .assertLatestNetworkSuggestion(timeSuggestion) .verifySystemClockWasNotSetAndResetCallTracking(); } @Test - public void suggestionsFromGnssOriginNotInPriorityList_areIgnored() { + public void suggestionsFromGnssOriginNotInPriorityList_areNotUsed() { ConfigurationInternal configInternal = new ConfigurationInternal.Builder(CONFIG_AUTO_ENABLED) .setOriginPriorities(ORIGIN_TELEPHONY) @@ -1777,7 +1828,7 @@ public class TimeDetectorStrategyImplTest { } @Test - public void suggestionsFromExternalOriginNotInPriorityList_areIgnored() { + public void suggestionsFromExternalOriginNotInPriorityList_areNotUsed() { ConfigurationInternal configInternal = new ConfigurationInternal.Builder(CONFIG_AUTO_ENABLED) .setOriginPriorities(ORIGIN_TELEPHONY) @@ -2015,6 +2066,11 @@ public class TimeDetectorStrategyImplTest { return this; } + Script simulateClearLatestNetworkSuggestion() { + mTimeDetectorStrategy.clearLatestNetworkSuggestion(); + return this; + } + Script simulateGnssTimeSuggestion(GnssTimeSuggestion timeSuggestion) { mTimeDetectorStrategy.suggestGnssTime(timeSuggestion); return this; @@ -2056,6 +2112,12 @@ public class TimeDetectorStrategyImplTest { return this; } + /** Calls {@link TimeDetectorStrategy#confirmTime(UnixEpochTime)}. */ + Script simulateConfirmTime(UnixEpochTime confirmationTime, boolean expectedReturnValue) { + assertEquals(expectedReturnValue, mTimeDetectorStrategy.confirmTime(confirmationTime)); + return this; + } + Script verifySystemClockWasNotSetAndResetCallTracking() { mFakeEnvironment.verifySystemClockNotSet(); mFakeEnvironment.resetCallTracking(); @@ -2218,11 +2280,6 @@ public class TimeDetectorStrategyImplTest { long calculateTimeInMillisForNow(UnixEpochTime unixEpochTime) { return unixEpochTime.at(peekElapsedRealtimeMillis()).getUnixEpochTimeMillis(); } - - Script simulateConfirmTime(UnixEpochTime confirmationTime, boolean expectedReturnValue) { - assertEquals(expectedReturnValue, mTimeDetectorStrategy.confirmTime(confirmationTime)); - return this; - } } private static TelephonyTimeSuggestion createTelephonyTimeSuggestion(int slotIndex,