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
This commit is contained in:
Neil Fuller
2022-12-20 13:47:58 +00:00
parent 4f84bb7b2d
commit 20d3b3c4c3
9 changed files with 193 additions and 20 deletions

View File

@@ -121,7 +121,7 @@ public final class UnixEpochTime implements Parcelable {
@Override
public String toString() {
return "UnixEpochTime{"
+ "mElapsedRealtimeTimeMillis=" + mElapsedRealtimeMillis
+ "mElapsedRealtimeMillis=" + mElapsedRealtimeMillis
+ ", mUnixEpochTimeMillis=" + mUnixEpochTimeMillis
+ '}';
}

View File

@@ -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

View File

@@ -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}.
*
* <p>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);

View File

@@ -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 <unix epoch time options>\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();

View File

@@ -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);

View File

@@ -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.
*/

View File

@@ -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) {
}

View File

@@ -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(

View File

@@ -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,