Merge "Further refactoring for multiple NTP servers"
This commit is contained in:
committed by
Android (Google) Code Review
commit
326fa474f9
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.net;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.net.sntp.Duration64;
|
||||
import android.net.sntp.Timestamp64;
|
||||
@@ -29,6 +30,7 @@ import com.android.internal.util.TrafficStatsConstants;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
@@ -39,9 +41,7 @@ import java.util.Random;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*
|
||||
* Simple SNTP client class for retrieving network time.
|
||||
* Simple, single-use SNTP client class for retrieving network time.
|
||||
*
|
||||
* Sample usage:
|
||||
* <pre>SntpClient client = new SntpClient();
|
||||
@@ -49,6 +49,10 @@ import java.util.function.Supplier;
|
||||
* long now = client.getNtpTime() + SystemClock.elapsedRealtime() - client.getNtpTimeReference();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>This class is not thread-safe.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class SntpClient {
|
||||
private static final String TAG = "SntpClient";
|
||||
@@ -87,6 +91,9 @@ public class SntpClient {
|
||||
// The round trip (network) time in milliseconds
|
||||
private long mRoundTripTime;
|
||||
|
||||
// Details of the NTP server used to obtain the time last.
|
||||
@Nullable private InetSocketAddress mServerSocketAddress;
|
||||
|
||||
private static class InvalidServerReplyException extends Exception {
|
||||
public InvalidServerReplyException(String message) {
|
||||
super(message);
|
||||
@@ -202,6 +209,7 @@ public class SntpClient {
|
||||
mNtpTime = responseTime.plus(clockOffsetDuration).toEpochMilli();
|
||||
mNtpTimeReference = responseTicks;
|
||||
mRoundTripTime = roundTripTimeMillis;
|
||||
mServerSocketAddress = new InetSocketAddress(address, port);
|
||||
} catch (Exception e) {
|
||||
EventLogTags.writeNtpFailure(address.toString(), e.toString());
|
||||
if (DBG) Log.d(TAG, "request time failed: " + e);
|
||||
@@ -284,6 +292,14 @@ public class SntpClient {
|
||||
return mRoundTripTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of the NTP server used in the NTP transaction
|
||||
*/
|
||||
@Nullable
|
||||
public InetSocketAddress getServerSocketAddress() {
|
||||
return mServerSocketAddress;
|
||||
}
|
||||
|
||||
private static void checkValidServerReply(
|
||||
byte leap, byte mode, int stratum, Timestamp64 transmitTimestamp,
|
||||
Timestamp64 referenceTimestamp, Timestamp64 randomizedRequestTimestamp,
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.time.Duration;
|
||||
@@ -50,7 +51,7 @@ import java.util.function.Supplier;
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class NtpTrustedTime implements TrustedTime {
|
||||
public abstract class NtpTrustedTime implements TrustedTime {
|
||||
|
||||
private static final String URI_SCHEME_NTP = "ntp";
|
||||
|
||||
@@ -107,16 +108,19 @@ public class NtpTrustedTime implements TrustedTime {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static class TimeResult {
|
||||
public static final class TimeResult {
|
||||
private final long mUnixEpochTimeMillis;
|
||||
private final long mElapsedRealtimeMillis;
|
||||
private final int mUncertaintyMillis;
|
||||
@NonNull private final InetSocketAddress mNtpServerSocketAddress;
|
||||
|
||||
public TimeResult(
|
||||
long unixEpochTimeMillis, long elapsedRealtimeMillis, int uncertaintyMillis) {
|
||||
long unixEpochTimeMillis, long elapsedRealtimeMillis, int uncertaintyMillis,
|
||||
@NonNull InetSocketAddress ntpServerSocketAddress) {
|
||||
mUnixEpochTimeMillis = unixEpochTimeMillis;
|
||||
mElapsedRealtimeMillis = elapsedRealtimeMillis;
|
||||
mUncertaintyMillis = uncertaintyMillis;
|
||||
mNtpServerSocketAddress = Objects.requireNonNull(ntpServerSocketAddress);
|
||||
}
|
||||
|
||||
public long getTimeMillis() {
|
||||
@@ -152,12 +156,35 @@ public class NtpTrustedTime implements TrustedTime {
|
||||
return currentElapsedRealtimeMillis - mElapsedRealtimeMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof TimeResult)) {
|
||||
return false;
|
||||
}
|
||||
TimeResult that = (TimeResult) o;
|
||||
return mUnixEpochTimeMillis == that.mUnixEpochTimeMillis
|
||||
&& mElapsedRealtimeMillis == that.mElapsedRealtimeMillis
|
||||
&& mUncertaintyMillis == that.mUncertaintyMillis
|
||||
&& mNtpServerSocketAddress.equals(
|
||||
that.mNtpServerSocketAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mUnixEpochTimeMillis, mElapsedRealtimeMillis, mUncertaintyMillis,
|
||||
mNtpServerSocketAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TimeResult{"
|
||||
+ "unixEpochTime=" + Instant.ofEpochMilli(mUnixEpochTimeMillis)
|
||||
+ ", elapsedRealtime=" + Duration.ofMillis(mElapsedRealtimeMillis)
|
||||
+ ", mUncertaintyMillis=" + mUncertaintyMillis
|
||||
+ ", mNtpServerSocketAddress=" + mNtpServerSocketAddress
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -167,46 +194,23 @@ public class NtpTrustedTime implements TrustedTime {
|
||||
|
||||
private static NtpTrustedTime sSingleton;
|
||||
|
||||
@NonNull
|
||||
private final Context mContext;
|
||||
|
||||
/**
|
||||
* A supplier that returns the ConnectivityManager. The Supplier can return null if
|
||||
* ConnectivityService isn't running yet.
|
||||
*/
|
||||
private final Supplier<ConnectivityManager> mConnectivityManagerSupplier =
|
||||
new Supplier<ConnectivityManager>() {
|
||||
private ConnectivityManager mConnectivityManager;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public synchronized ConnectivityManager get() {
|
||||
// We can't do this at initialization time: ConnectivityService might not be running
|
||||
// yet.
|
||||
if (mConnectivityManager == null) {
|
||||
mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
|
||||
}
|
||||
return mConnectivityManager;
|
||||
}
|
||||
};
|
||||
|
||||
/** An in-memory config override for use during tests. */
|
||||
@GuardedBy("this")
|
||||
@Nullable
|
||||
private NtpConfig mNtpConfigForTests;
|
||||
|
||||
// Declared volatile and accessed outside of synchronized blocks to avoid blocking reads during
|
||||
// Declared volatile and accessed outside synchronized blocks to avoid blocking reads during
|
||||
// forceRefresh().
|
||||
private volatile TimeResult mTimeResult;
|
||||
|
||||
private NtpTrustedTime(Context context) {
|
||||
mContext = Objects.requireNonNull(context);
|
||||
protected NtpTrustedTime() {
|
||||
}
|
||||
|
||||
@UnsupportedAppUsage
|
||||
public static synchronized NtpTrustedTime getInstance(Context context) {
|
||||
if (sSingleton == null) {
|
||||
Context appContext = context.getApplicationContext();
|
||||
sSingleton = new NtpTrustedTime(appContext);
|
||||
sSingleton = new NtpTrustedTimeImpl(appContext);
|
||||
}
|
||||
return sSingleton;
|
||||
}
|
||||
@@ -224,65 +228,71 @@ public class NtpTrustedTime implements TrustedTime {
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
||||
public boolean forceRefresh() {
|
||||
synchronized (this) {
|
||||
NtpConfig connectionInfo = getNtpConfig();
|
||||
if (connectionInfo == null) {
|
||||
NtpConfig ntpConfig = getNtpConfig();
|
||||
if (ntpConfig == null) {
|
||||
// missing server config, so no NTP time available
|
||||
if (LOGD) Log.d(TAG, "forceRefresh: invalid server config");
|
||||
return false;
|
||||
}
|
||||
|
||||
ConnectivityManager connectivityManager = mConnectivityManagerSupplier.get();
|
||||
if (connectivityManager == null) {
|
||||
if (LOGD) Log.d(TAG, "forceRefresh: no ConnectivityManager");
|
||||
return false;
|
||||
}
|
||||
final Network network = connectivityManager.getActiveNetwork();
|
||||
final NetworkInfo ni = connectivityManager.getNetworkInfo(network);
|
||||
|
||||
// This connectivity check is to avoid performing a DNS lookup for the time server on a
|
||||
// unconnected network. There are races to obtain time in Android when connectivity
|
||||
// changes, which means that forceRefresh() can be called by various components before
|
||||
// the network is actually available. This led in the past to DNS lookup failures being
|
||||
// cached (~2 seconds) thereby preventing the device successfully making an NTP request
|
||||
// when connectivity had actually been established.
|
||||
// A side effect of check is that tests that run a fake NTP server on the device itself
|
||||
// will only be able to use it if the active network is connected, even though loopback
|
||||
// addresses are actually reachable.
|
||||
if (ni == null || !ni.isConnected()) {
|
||||
if (LOGD) Log.d(TAG, "forceRefresh: no connectivity");
|
||||
Network network = getNetwork();
|
||||
if (network == null) {
|
||||
if (LOGD) Log.d(TAG, "forceRefresh: no network available");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
|
||||
final SntpClient client = new SntpClient();
|
||||
final URI ntpServerUri = connectionInfo.getServerUri();
|
||||
final String serverName = ntpServerUri.getHost();
|
||||
final int port = ntpServerUri.getPort() == -1
|
||||
? SntpClient.STANDARD_NTP_PORT : ntpServerUri.getPort();
|
||||
final int timeoutMillis = saturatedCast(connectionInfo.getTimeout().toMillis());
|
||||
if (client.requestTime(serverName, port, timeoutMillis, network)) {
|
||||
int ntpUncertaintyMillis = saturatedCast(client.getRoundTripTime() / 2);
|
||||
mTimeResult = new TimeResult(
|
||||
client.getNtpTime(), client.getNtpTimeReference(), ntpUncertaintyMillis);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
if (LOGD) {
|
||||
Log.d(TAG, "forceRefresh: NTP request network=" + network
|
||||
+ " ntpConfig=" + ntpConfig);
|
||||
}
|
||||
TimeResult timeResult =
|
||||
queryNtpServer(network, ntpConfig.getServerUri(), ntpConfig.getTimeout());
|
||||
if (timeResult != null) {
|
||||
// Keep any previous time result.
|
||||
mTimeResult = timeResult;
|
||||
}
|
||||
return timeResult != null;
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
private NtpConfig getNtpConfig() {
|
||||
if (mNtpConfigForTests != null) {
|
||||
return mNtpConfigForTests;
|
||||
}
|
||||
return getNtpConfigInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts a {@code long} to an {@code int}, clamping the value within the int range.
|
||||
* Returns the {@link NtpConfig} to use during an NTP query. This method can return {@code null}
|
||||
* if there is no config, or the config found is invalid.
|
||||
*
|
||||
* <p>This method has been made public for easy replacement during tests.
|
||||
*/
|
||||
private static int saturatedCast(long longValue) {
|
||||
if (longValue > Integer.MAX_VALUE) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
if (longValue < Integer.MIN_VALUE) {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
return (int) longValue;
|
||||
}
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
public abstract NtpConfig getNtpConfigInternal();
|
||||
|
||||
/**
|
||||
* Returns the {@link Network} to use during an NTP query. This method can return {@code null}
|
||||
* if there is no connectivity
|
||||
*
|
||||
* <p>This method has been made public for easy replacement during tests.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
public abstract Network getNetwork();
|
||||
|
||||
/**
|
||||
* Queries the specified NTP server. This is a blocking call. Returns {@code null} if the query
|
||||
* fails.
|
||||
*
|
||||
* <p>This method has been made public for easy replacement during tests.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
public abstract TimeResult queryNtpServer(
|
||||
@NonNull Network network, @NonNull URI ntpServerUri, @NonNull Duration timeout);
|
||||
|
||||
/**
|
||||
* Only kept for UnsupportedAppUsage.
|
||||
@@ -371,39 +381,6 @@ public class NtpTrustedTime implements TrustedTime {
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
private NtpConfig getNtpConfig() {
|
||||
if (mNtpConfigForTests != null) {
|
||||
return mNtpConfigForTests;
|
||||
}
|
||||
|
||||
final ContentResolver resolver = mContext.getContentResolver();
|
||||
final Resources res = mContext.getResources();
|
||||
|
||||
// The Settings value has priority over static config. Check settings first.
|
||||
final String serverGlobalSetting =
|
||||
Settings.Global.getString(resolver, Settings.Global.NTP_SERVER);
|
||||
final URI settingsServerInfo = parseNtpServerSetting(serverGlobalSetting);
|
||||
|
||||
URI ntpServerUri;
|
||||
if (settingsServerInfo != null) {
|
||||
ntpServerUri = settingsServerInfo;
|
||||
} else {
|
||||
String configValue = res.getString(com.android.internal.R.string.config_ntpServer);
|
||||
try {
|
||||
ntpServerUri = parseNtpUriStrict(configValue);
|
||||
} catch (URISyntaxException e) {
|
||||
ntpServerUri = null;
|
||||
}
|
||||
}
|
||||
|
||||
final int defaultTimeoutMillis =
|
||||
res.getInteger(com.android.internal.R.integer.config_ntpTimeout);
|
||||
final Duration timeout = Duration.ofMillis(Settings.Global.getInt(
|
||||
resolver, Settings.Global.NTP_TIMEOUT, defaultTimeoutMillis));
|
||||
return ntpServerUri == null ? null : new NtpConfig(ntpServerUri, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and returns an NTP server config URI, or throws an exception if the URI doesn't
|
||||
* conform to expectations.
|
||||
@@ -487,4 +464,129 @@ public class NtpTrustedTime implements TrustedTime {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The real implementation of {@link NtpTrustedTime}. Contains the parts that are more difficult
|
||||
* to test.
|
||||
*/
|
||||
private static final class NtpTrustedTimeImpl extends NtpTrustedTime {
|
||||
|
||||
/**
|
||||
* A supplier that returns the ConnectivityManager. The Supplier can return null if
|
||||
* ConnectivityService isn't running yet.
|
||||
*/
|
||||
private final Supplier<ConnectivityManager> mConnectivityManagerSupplier =
|
||||
new Supplier<>() {
|
||||
private ConnectivityManager mConnectivityManager;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public synchronized ConnectivityManager get() {
|
||||
// We can't do this at initialization time: ConnectivityService might not be running
|
||||
// yet.
|
||||
if (mConnectivityManager == null) {
|
||||
mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
|
||||
}
|
||||
return mConnectivityManager;
|
||||
}
|
||||
};
|
||||
|
||||
@NonNull
|
||||
private final Context mContext;
|
||||
|
||||
private NtpTrustedTimeImpl(@NonNull Context context) {
|
||||
mContext = Objects.requireNonNull(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
public NtpConfig getNtpConfigInternal() {
|
||||
final ContentResolver resolver = mContext.getContentResolver();
|
||||
final Resources res = mContext.getResources();
|
||||
|
||||
// The Settings value has priority over static config. Check settings first.
|
||||
final String serverGlobalSetting =
|
||||
Settings.Global.getString(resolver, Settings.Global.NTP_SERVER);
|
||||
final URI settingsServerInfo = parseNtpServerSetting(serverGlobalSetting);
|
||||
|
||||
URI ntpServerUri;
|
||||
if (settingsServerInfo != null) {
|
||||
ntpServerUri = settingsServerInfo;
|
||||
} else {
|
||||
String configValue = res.getString(com.android.internal.R.string.config_ntpServer);
|
||||
try {
|
||||
ntpServerUri = parseNtpUriStrict(configValue);
|
||||
} catch (URISyntaxException e) {
|
||||
ntpServerUri = null;
|
||||
}
|
||||
}
|
||||
|
||||
final int defaultTimeoutMillis =
|
||||
res.getInteger(com.android.internal.R.integer.config_ntpTimeout);
|
||||
final Duration timeout = Duration.ofMillis(Settings.Global.getInt(
|
||||
resolver, Settings.Global.NTP_TIMEOUT, defaultTimeoutMillis));
|
||||
return ntpServerUri == null ? null : new NtpConfig(ntpServerUri, timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Network getNetwork() {
|
||||
ConnectivityManager connectivityManager = mConnectivityManagerSupplier.get();
|
||||
if (connectivityManager == null) {
|
||||
if (LOGD) Log.d(TAG, "getNetwork: no ConnectivityManager");
|
||||
return null;
|
||||
}
|
||||
final Network network = connectivityManager.getActiveNetwork();
|
||||
final NetworkInfo ni = connectivityManager.getNetworkInfo(network);
|
||||
|
||||
// This connectivity check is to avoid performing a DNS lookup for the time server on a
|
||||
// unconnected network. There are races to obtain time in Android when connectivity
|
||||
// changes, which means that forceRefresh() can be called by various components before
|
||||
// the network is actually available. This led in the past to DNS lookup failures being
|
||||
// cached (~2 seconds) thereby preventing the device successfully making an NTP request
|
||||
// when connectivity had actually been established.
|
||||
// A side effect of check is that tests that run a fake NTP server on the device itself
|
||||
// will only be able to use it if the active network is connected, even though loopback
|
||||
// addresses are actually reachable.
|
||||
if (ni == null || !ni.isConnected()) {
|
||||
if (LOGD) Log.d(TAG, "getNetwork: no connectivity");
|
||||
return null;
|
||||
}
|
||||
return network;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public TimeResult queryNtpServer(
|
||||
@NonNull Network network, @NonNull URI ntpServerUri, @NonNull Duration timeout) {
|
||||
|
||||
final SntpClient client = new SntpClient();
|
||||
final String serverName = ntpServerUri.getHost();
|
||||
final int port = ntpServerUri.getPort() == -1
|
||||
? SntpClient.STANDARD_NTP_PORT : ntpServerUri.getPort();
|
||||
final int timeoutMillis = saturatedCast(timeout.toMillis());
|
||||
if (client.requestTime(serverName, port, timeoutMillis, network)) {
|
||||
int ntpUncertaintyMillis = saturatedCast(client.getRoundTripTime() / 2);
|
||||
InetSocketAddress ntpServerSocketAddress = client.getServerSocketAddress();
|
||||
return new TimeResult(
|
||||
client.getNtpTime(), client.getNtpTimeReference(), ntpUncertaintyMillis,
|
||||
ntpServerSocketAddress);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts a {@code long} to an {@code int}, clamping the value within the int range.
|
||||
*/
|
||||
private static int saturatedCast(long longValue) {
|
||||
if (longValue > Integer.MAX_VALUE) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
if (longValue < Integer.MIN_VALUE) {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
return (int) longValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,20 @@
|
||||
package android.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.net.Network;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
@@ -26,6 +37,7 @@ import androidx.test.filters.SmallTest;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.time.Duration;
|
||||
@@ -138,4 +150,158 @@ public class NtpTrustedTimeTest {
|
||||
public void testNtpConfig_negativeTimeout() {
|
||||
new NtpTrustedTime.NtpConfig(VALID_SERVER_URI, Duration.ofMillis(-1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceRefresh_nullConfig() {
|
||||
NtpTrustedTime ntpTrustedTime = spy(NtpTrustedTime.class);
|
||||
when(ntpTrustedTime.getNtpConfigInternal()).thenReturn(null);
|
||||
|
||||
assertFalse(ntpTrustedTime.forceRefresh());
|
||||
|
||||
assertFalse(ntpTrustedTime.hasCache());
|
||||
assertEquals(0, ntpTrustedTime.getCachedNtpTime());
|
||||
assertEquals(0, ntpTrustedTime.getCachedNtpTimeReference());
|
||||
assertEquals(Long.MAX_VALUE, ntpTrustedTime.getCacheAge());
|
||||
assertNull(ntpTrustedTime.getCachedTimeResult());
|
||||
|
||||
verify(ntpTrustedTime, times(1)).getNtpConfigInternal();
|
||||
verify(ntpTrustedTime, never()).getNetwork();
|
||||
verify(ntpTrustedTime, never()).queryNtpServer(any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceRefresh_noConnectivity() {
|
||||
NtpTrustedTime ntpTrustedTime = spy(NtpTrustedTime.class);
|
||||
URI serverUri = URI.create("ntp://ntpserver.name");
|
||||
Duration timeout = Duration.ofSeconds(5);
|
||||
when(ntpTrustedTime.getNtpConfigInternal()).thenReturn(
|
||||
new NtpTrustedTime.NtpConfig(serverUri, timeout));
|
||||
|
||||
when(ntpTrustedTime.getNetwork()).thenReturn(null);
|
||||
|
||||
assertFalse(ntpTrustedTime.forceRefresh());
|
||||
|
||||
assertFalse(ntpTrustedTime.hasCache());
|
||||
assertEquals(0, ntpTrustedTime.getCachedNtpTime());
|
||||
assertEquals(0, ntpTrustedTime.getCachedNtpTimeReference());
|
||||
assertEquals(Long.MAX_VALUE, ntpTrustedTime.getCacheAge());
|
||||
assertNull(ntpTrustedTime.getCachedTimeResult());
|
||||
|
||||
verify(ntpTrustedTime, times(1)).getNtpConfigInternal();
|
||||
verify(ntpTrustedTime, times(1)).getNetwork();
|
||||
verify(ntpTrustedTime, never()).queryNtpServer(any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceRefresh_queryFailed() {
|
||||
NtpTrustedTime ntpTrustedTime = spy(NtpTrustedTime.class);
|
||||
URI serverUri = URI.create("ntp://ntpserver.name");
|
||||
Duration timeout = Duration.ofSeconds(5);
|
||||
when(ntpTrustedTime.getNtpConfigInternal()).thenReturn(
|
||||
new NtpTrustedTime.NtpConfig(serverUri, timeout));
|
||||
|
||||
Network network = mock(Network.class);
|
||||
when(ntpTrustedTime.getNetwork()).thenReturn(network);
|
||||
|
||||
when(ntpTrustedTime.queryNtpServer(network, serverUri, timeout)).thenReturn(null);
|
||||
|
||||
assertFalse(ntpTrustedTime.forceRefresh());
|
||||
|
||||
assertFalse(ntpTrustedTime.hasCache());
|
||||
assertEquals(0, ntpTrustedTime.getCachedNtpTime());
|
||||
assertEquals(0, ntpTrustedTime.getCachedNtpTimeReference());
|
||||
assertEquals(Long.MAX_VALUE, ntpTrustedTime.getCacheAge());
|
||||
assertNull(ntpTrustedTime.getCachedTimeResult());
|
||||
|
||||
verify(ntpTrustedTime, times(1)).getNtpConfigInternal();
|
||||
verify(ntpTrustedTime, times(1)).getNetwork();
|
||||
verify(ntpTrustedTime, times(1)).queryNtpServer(network, serverUri, timeout);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceRefresh_querySucceeded() {
|
||||
NtpTrustedTime ntpTrustedTime = spy(NtpTrustedTime.class);
|
||||
URI serverUri = URI.create("ntp://ntpserver.name");
|
||||
Duration timeout = Duration.ofSeconds(5);
|
||||
when(ntpTrustedTime.getNtpConfigInternal()).thenReturn(
|
||||
new NtpTrustedTime.NtpConfig(serverUri, timeout));
|
||||
|
||||
Network network = mock(Network.class);
|
||||
when(ntpTrustedTime.getNetwork()).thenReturn(network);
|
||||
|
||||
NtpTrustedTime.TimeResult successResult = new NtpTrustedTime.TimeResult(123L, 456L, 789,
|
||||
InetSocketAddress.createUnresolved("placeholder", 123));
|
||||
when(ntpTrustedTime.queryNtpServer(network, serverUri, timeout)).thenReturn(successResult);
|
||||
|
||||
assertTrue(ntpTrustedTime.forceRefresh());
|
||||
|
||||
assertTrue(ntpTrustedTime.hasCache());
|
||||
assertEquals(successResult.getTimeMillis(), ntpTrustedTime.getCachedNtpTime());
|
||||
assertEquals(successResult.getElapsedRealtimeMillis(),
|
||||
ntpTrustedTime.getCachedNtpTimeReference());
|
||||
assertTrue(ntpTrustedTime.getCacheAge() != Long.MAX_VALUE);
|
||||
assertEquals(successResult, ntpTrustedTime.getCachedTimeResult());
|
||||
|
||||
verify(ntpTrustedTime, times(1)).getNtpConfigInternal();
|
||||
verify(ntpTrustedTime, times(1)).getNetwork();
|
||||
verify(ntpTrustedTime, times(1)).queryNtpServer(network, serverUri, timeout);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceRefresh_keepsOldValueOnFailure() {
|
||||
NtpTrustedTime ntpTrustedTime = spy(NtpTrustedTime.class);
|
||||
URI serverUri = URI.create("ntp://ntpserver.name");
|
||||
Duration timeout = Duration.ofSeconds(5);
|
||||
when(ntpTrustedTime.getNtpConfigInternal()).thenReturn(
|
||||
new NtpTrustedTime.NtpConfig(serverUri, timeout));
|
||||
|
||||
Network network = mock(Network.class);
|
||||
when(ntpTrustedTime.getNetwork()).thenReturn(network);
|
||||
|
||||
NtpTrustedTime.TimeResult successResult = new NtpTrustedTime.TimeResult(123L, 456L, 789,
|
||||
InetSocketAddress.createUnresolved("placeholder", 123));
|
||||
when(ntpTrustedTime.queryNtpServer(network, serverUri, timeout)).thenReturn(successResult);
|
||||
|
||||
assertTrue(ntpTrustedTime.forceRefresh());
|
||||
|
||||
assertTrue(ntpTrustedTime.hasCache());
|
||||
assertEquals(successResult, ntpTrustedTime.getCachedTimeResult());
|
||||
|
||||
when(ntpTrustedTime.queryNtpServer(network, serverUri, timeout)).thenReturn(null);
|
||||
|
||||
assertFalse(ntpTrustedTime.forceRefresh());
|
||||
|
||||
assertTrue(ntpTrustedTime.hasCache());
|
||||
assertEquals(successResult, ntpTrustedTime.getCachedTimeResult());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceRefresh_keepsNewValueOnSuccess() {
|
||||
NtpTrustedTime ntpTrustedTime = spy(NtpTrustedTime.class);
|
||||
URI serverUri = URI.create("ntp://ntpserver.name");
|
||||
Duration timeout = Duration.ofSeconds(5);
|
||||
when(ntpTrustedTime.getNtpConfigInternal()).thenReturn(
|
||||
new NtpTrustedTime.NtpConfig(serverUri, timeout));
|
||||
|
||||
Network network = mock(Network.class);
|
||||
when(ntpTrustedTime.getNetwork()).thenReturn(network);
|
||||
|
||||
NtpTrustedTime.TimeResult successResult1 = new NtpTrustedTime.TimeResult(123L, 456L, 789,
|
||||
InetSocketAddress.createUnresolved("placeholder", 123));
|
||||
when(ntpTrustedTime.queryNtpServer(network, serverUri, timeout)).thenReturn(successResult1);
|
||||
|
||||
assertTrue(ntpTrustedTime.forceRefresh());
|
||||
|
||||
assertTrue(ntpTrustedTime.hasCache());
|
||||
assertEquals(successResult1, ntpTrustedTime.getCachedTimeResult());
|
||||
|
||||
NtpTrustedTime.TimeResult successResult2 = new NtpTrustedTime.TimeResult(123L, 456L, 789,
|
||||
InetSocketAddress.createUnresolved("placeholder", 123));
|
||||
when(ntpTrustedTime.queryNtpServer(network, serverUri, timeout)).thenReturn(successResult2);
|
||||
|
||||
assertTrue(ntpTrustedTime.forceRefresh());
|
||||
|
||||
assertTrue(ntpTrustedTime.hasCache());
|
||||
assertEquals(successResult2, ntpTrustedTime.getCachedTimeResult());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,6 +271,7 @@ public class NetworkTimeUpdateService extends Binder {
|
||||
NetworkTimeSuggestion timeSuggestion =
|
||||
new NetworkTimeSuggestion(timeSignal, ntpResult.getUncertaintyMillis());
|
||||
timeSuggestion.addDebugInfo(debugInfo);
|
||||
timeSuggestion.addDebugInfo(ntpResult.toString());
|
||||
mTimeDetectorInternal.suggestNetworkTime(timeSuggestion);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.time.Instant;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@@ -405,8 +406,8 @@ public class TimeDetectorServiceTest {
|
||||
|
||||
@Test
|
||||
public void testLatestNetworkTime() {
|
||||
NtpTrustedTime.TimeResult latestNetworkTime =
|
||||
new NtpTrustedTime.TimeResult(1234L, 54321L, 999);
|
||||
NtpTrustedTime.TimeResult latestNetworkTime = new NtpTrustedTime.TimeResult(
|
||||
1234L, 54321L, 999, InetSocketAddress.createUnresolved("test.timeserver", 123));
|
||||
when(mMockNtpTrustedTime.getCachedTimeResult())
|
||||
.thenReturn(latestNetworkTime);
|
||||
TimePoint expected = new TimePoint(latestNetworkTime.getTimeMillis(),
|
||||
|
||||
Reference in New Issue
Block a user