From 030a692189bce6ccaf1b8e9378b486c784887de4 Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Thu, 3 Mar 2022 13:53:36 +0000 Subject: [PATCH] Make NTP config configurable from cmd Enable NTP failure / timeout testing by providing an ability for testers to change the NTP configuration. Also improve debugging information from dumpsys and logs. Testing: adb shell cmd network_time_update_service set_server_config --hostname localhost adb shell dumpsys network_time_update_service adb shell cmd network_time_update_service force_refresh adb shell dumpsys network_time_update_service adb shell cmd network_time_update_service set_server_config --hostname localhost --timeout_millis 1000 adb shell dumpsys network_time_update_service adb shell cmd network_time_update_service force_refresh adb shell cmd network_time_update_service set_server_config --hostname localhost --timeout_millis 10000 adb shell dumpsys network_time_update_service adb shell cmd network_time_update_service force_refresh adb shell cmd network_time_update_service set_server_config adb shell cmd network_time_update_service force_refresh Bug: 213393821 Test: See above Change-Id: Ic66c7fabe0b42a3d5c86f8b85f17096bf64a8209 --- core/java/android/util/NtpTrustedTime.java | 80 ++++++++++++++++--- .../server/NetworkTimeUpdateService.java | 35 +++++--- .../NetworkTimeUpdateServiceShellCommand.java | 39 +++++++++ 3 files changed, 129 insertions(+), 25 deletions(-) diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java index 40beab3235762..aebc5e86605a9 100644 --- a/core/java/android/util/NtpTrustedTime.java +++ b/core/java/android/util/NtpTrustedTime.java @@ -33,6 +33,9 @@ import android.text.TextUtils; import com.android.internal.annotations.GuardedBy; +import java.io.PrintWriter; +import java.time.Duration; +import java.time.Instant; import java.util.Objects; import java.util.function.Supplier; @@ -96,8 +99,8 @@ public class NtpTrustedTime implements TrustedTime { @Override public String toString() { return "TimeResult{" - + "mTimeMillis=" + mTimeMillis - + ", mElapsedRealtimeMillis=" + mElapsedRealtimeMillis + + "mTimeMillis=" + Instant.ofEpochMilli(mTimeMillis) + + ", mElapsedRealtimeMillis=" + Duration.ofMillis(mElapsedRealtimeMillis) + ", mCertaintyMillis=" + mCertaintyMillis + '}'; } @@ -131,6 +134,14 @@ public class NtpTrustedTime implements TrustedTime { } }; + /** An in-memory config override for use during tests. */ + @Nullable + private String mHostnameForTests; + + /** An in-memory config override for use during tests. */ + @Nullable + private Duration mTimeoutForTests; + // Declared volatile and accessed outside of synchronized blocks to avoid blocking reads during // forceRefresh(). private volatile TimeResult mTimeResult; @@ -148,12 +159,23 @@ public class NtpTrustedTime implements TrustedTime { return sSingleton; } + /** + * Overrides the NTP server config for tests. Passing {@code null} to a parameter clears the + * test value, i.e. so the normal value will be used next time. + */ + public void setServerConfigForTests(@Nullable String hostname, @Nullable Duration timeout) { + synchronized (this) { + mHostnameForTests = hostname; + mTimeoutForTests = timeout; + } + } + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean forceRefresh() { synchronized (this) { NtpConnectionInfo connectionInfo = getNtpConnectionInfo(); if (connectionInfo == null) { - // missing server config, so no trusted time available + // missing server config, so no NTP time available if (LOGD) Log.d(TAG, "forceRefresh: invalid server config"); return false; } @@ -290,6 +312,14 @@ public class NtpTrustedTime implements TrustedTime { int getTimeoutMillis() { return mTimeoutMillis; } + + @Override + public String toString() { + return "NtpConnectionInfo{" + + "mServer='" + mServer + '\'' + + ", mTimeoutMillis=" + mTimeoutMillis + + '}'; + } } @GuardedBy("this") @@ -297,17 +327,41 @@ public class NtpTrustedTime implements TrustedTime { final ContentResolver resolver = mContext.getContentResolver(); final Resources res = mContext.getResources(); - final String defaultServer = res.getString( - com.android.internal.R.string.config_ntpServer); - final int defaultTimeoutMillis = res.getInteger( - com.android.internal.R.integer.config_ntpTimeout); - final String secureServer = Settings.Global.getString( - resolver, Settings.Global.NTP_SERVER); - final int timeoutMillis = Settings.Global.getInt( - resolver, Settings.Global.NTP_TIMEOUT, defaultTimeoutMillis); + final String hostname; + if (mHostnameForTests != null) { + hostname = mHostnameForTests; + } else { + String serverGlobalSetting = + Settings.Global.getString(resolver, Settings.Global.NTP_SERVER); + if (serverGlobalSetting != null) { + hostname = serverGlobalSetting; + } else { + hostname = res.getString(com.android.internal.R.string.config_ntpServer); + } + } - final String server = secureServer != null ? secureServer : defaultServer; - return TextUtils.isEmpty(server) ? null : new NtpConnectionInfo(server, timeoutMillis); + final int timeoutMillis; + if (mTimeoutForTests != null) { + timeoutMillis = (int) mTimeoutForTests.toMillis(); + } else { + int defaultTimeoutMillis = + res.getInteger(com.android.internal.R.integer.config_ntpTimeout); + timeoutMillis = Settings.Global.getInt( + resolver, Settings.Global.NTP_TIMEOUT, defaultTimeoutMillis); + } + return TextUtils.isEmpty(hostname) ? null : new NtpConnectionInfo(hostname, timeoutMillis); + } + + /** Prints debug information. */ + public void dump(PrintWriter pw) { + synchronized (this) { + pw.println("getNtpConnectionInfo()=" + getNtpConnectionInfo()); + pw.println("mTimeResult=" + mTimeResult); + if (mTimeResult != null) { + pw.println("mTimeResult.getAgeMillis()=" + + Duration.ofMillis(mTimeResult.getAgeMillis())); + } + } } } diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java index c73ce598c1068..e0adee9fcb324 100644 --- a/services/core/java/com/android/server/NetworkTimeUpdateService.java +++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java @@ -17,6 +17,7 @@ package com.android.server; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.timedetector.NetworkTimeSuggestion; @@ -45,12 +46,12 @@ import android.util.LocalLog; import android.util.Log; import android.util.NtpTrustedTime; import android.util.NtpTrustedTime.TimeResult; -import android.util.TimeUtils; import com.android.internal.util.DumpUtils; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.time.Duration; /** * Monitors the network time. If looking up the network time fails for some reason, it tries a few @@ -191,6 +192,19 @@ public class NetworkTimeUpdateService extends Binder { return success; } + /** + * Overrides the NTP server config for tests. Passing {@code null} to a parameter clears the + * test value, i.e. so the normal value will be used next time. + */ + void setServerConfigForTests(@Nullable String hostname, @Nullable Duration timeout) { + mContext.enforceCallingPermission( + android.Manifest.permission.SET_TIME, "set NTP server config for tests"); + + mLocalLog.log("Setting server config for tests: hostname=" + hostname + + ", timeout=" + timeout); + mTime.setServerConfigForTests(hostname, timeout); + } + private void onPollNetworkTime(int event) { // If we don't have any default network, don't bother. if (mDefaultNetwork == null) return; @@ -346,17 +360,14 @@ public class NetworkTimeUpdateService extends Binder { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - pw.print("PollingIntervalMs: "); - TimeUtils.formatDuration(mPollingIntervalMs, pw); - pw.print("\nPollingIntervalShorterMs: "); - TimeUtils.formatDuration(mPollingIntervalShorterMs, pw); - pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax); - pw.println("\nTryAgainCounter: " + mTryAgainCounter); - NtpTrustedTime.TimeResult ntpResult = mTime.getCachedTimeResult(); - pw.println("NTP cache result: " + ntpResult); - if (ntpResult != null) { - pw.println("NTP result age: " + ntpResult.getAgeMillis()); - } + pw.println("mPollingIntervalMs=" + Duration.ofMillis(mPollingIntervalMs)); + pw.println("mPollingIntervalShorterMs=" + Duration.ofMillis(mPollingIntervalShorterMs)); + pw.println("mTryAgainTimesMax=" + mTryAgainTimesMax); + pw.println("mTryAgainCounter=" + mTryAgainCounter); + pw.println(); + pw.println("NtpTrustedTime:"); + mTime.dump(pw); + pw.println(); pw.println("Local logs:"); mLocalLog.dump(fd, pw, args); pw.println(); diff --git a/services/core/java/com/android/server/NetworkTimeUpdateServiceShellCommand.java b/services/core/java/com/android/server/NetworkTimeUpdateServiceShellCommand.java index dc93023d82c59..464af01a009a1 100644 --- a/services/core/java/com/android/server/NetworkTimeUpdateServiceShellCommand.java +++ b/services/core/java/com/android/server/NetworkTimeUpdateServiceShellCommand.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.os.ShellCommand; import java.io.PrintWriter; +import java.time.Duration; import java.util.Objects; /** Implements the shell command interface for {@link NetworkTimeUpdateService}. */ @@ -40,6 +41,13 @@ class NetworkTimeUpdateServiceShellCommand extends ShellCommand { */ private static final String SHELL_COMMAND_FORCE_REFRESH = "force_refresh"; + /** + * A shell command that sets the NTP server config for tests. Config is cleared on reboot. + */ + private static final String SHELL_COMMAND_SET_SERVER_CONFIG = "set_server_config"; + private static final String SET_SERVER_CONFIG_HOSTNAME_ARG = "--hostname"; + private static final String SET_SERVER_CONFIG_TIMEOUT_ARG = "--timeout_millis"; + @NonNull private final NetworkTimeUpdateService mNetworkTimeUpdateService; @@ -58,6 +66,8 @@ class NetworkTimeUpdateServiceShellCommand extends ShellCommand { return runClearTime(); case SHELL_COMMAND_FORCE_REFRESH: return runForceRefresh(); + case SHELL_COMMAND_SET_SERVER_CONFIG: + return runSetServerConfig(); default: { return handleDefaultCommands(cmd); } @@ -75,6 +85,29 @@ class NetworkTimeUpdateServiceShellCommand extends ShellCommand { return 0; } + private int runSetServerConfig() { + String hostname = null; + Duration timeout = null; + String opt; + while ((opt = getNextArg()) != null) { + switch (opt) { + case SET_SERVER_CONFIG_HOSTNAME_ARG: { + hostname = getNextArgRequired(); + break; + } + case SET_SERVER_CONFIG_TIMEOUT_ARG: { + timeout = Duration.ofMillis(Integer.parseInt(getNextArgRequired())); + break; + } + default: { + throw new IllegalArgumentException("Unknown option: " + opt); + } + } + } + mNetworkTimeUpdateService.setServerConfigForTests(hostname, timeout); + return 0; + } + @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); @@ -85,6 +118,12 @@ class NetworkTimeUpdateServiceShellCommand extends ShellCommand { pw.printf(" Clears the latest time.\n"); pw.printf(" %s\n", SHELL_COMMAND_FORCE_REFRESH); pw.printf(" Refreshes the latest time. Prints whether it was successful.\n"); + pw.printf(" %s\n", SHELL_COMMAND_SET_SERVER_CONFIG); + pw.printf(" Sets the NTP server config for tests. The config is not persisted.\n"); + pw.printf(" Options: [%s ] [%s ]\n", + SET_SERVER_CONFIG_HOSTNAME_ARG, SET_SERVER_CONFIG_TIMEOUT_ARG); + pw.printf(" Each key/value is optional and must be specified to override the\n"); + pw.printf(" normal value, not specifying a key causes it to reset to the original.\n"); pw.println(); } }