Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 030a6921 authored by Neil Fuller's avatar Neil Fuller
Browse files

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
<observe false>

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
<observe false, but faster>

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
<observe false, but slower>

adb shell cmd network_time_update_service set_server_config
adb shell cmd network_time_update_service force_refresh
<observe true>

Bug: 213393821
Test: See above
Change-Id: Ic66c7fabe0b42a3d5c86f8b85f17096bf64a8209
parent b368bac1
Loading
Loading
Loading
Loading
+69 −15
Original line number Diff line number Diff line
@@ -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(

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

        final String server = secureServer != null ? secureServer : defaultServer;
        return TextUtils.isEmpty(server) ? null : new NtpConnectionInfo(server, 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()));
            }
        }
    }
}
+23 −12
Original line number Diff line number Diff line
@@ -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();
+39 −0
Original line number Diff line number Diff line
@@ -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 <hostname>] [%s <millis>]\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();
    }
}