Retry alternative addresses on timeout in SNTP client

Changed the default timeout for SNTP requests to 1 second.

Issue: androidx/media#1540
PiperOrigin-RevId: 652897579
This commit is contained in:
rohks 2024-07-16 10:34:59 -07:00 committed by Copybara-Service
parent 7f304092ae
commit ded66debc3
2 changed files with 68 additions and 42 deletions

View File

@ -24,8 +24,8 @@
* `MediaCodecVideoRenderer` avoids decoding samples that are neither
rendered nor used as reference by other samples.
* Add `BasePreloadManager.Listener` to propagate preload events to apps.
* Allow changing SNTP client timeout
([#1540](https://github.com/androidx/media/issues/1540)).
* Allow changing SNTP client timeout and retry alternative addresses on
timeout ([#1540](https://github.com/androidx/media/issues/1540)).
* Remove `MediaCodecAdapter.Configuration.flags` as the field was always
zero.
* Transformer:

View File

@ -15,6 +15,8 @@
*/
package androidx.media3.exoplayer.util;
import static androidx.media3.common.util.Assertions.checkNotNull;
import android.os.SystemClock;
import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
@ -27,6 +29,7 @@ import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.ConcurrentModificationException;
@ -44,7 +47,7 @@ public final class SntpClient {
public static final String DEFAULT_NTP_HOST = "time.android.com";
/** The default maximum time, in milliseconds, to wait for the SNTP request to complete. */
public static final int DEFAULT_TIMEOUT_MS = 5_000;
public static final int DEFAULT_TIMEOUT_MS = 1_000;
/** Callback for calls to {@link #initialize(Loader, InitializationCallback)}. */
public interface InitializationCallback {
@ -60,6 +63,7 @@ public final class SntpClient {
void onInitializationFailed(IOException error);
}
private static final int MAX_RETRY_COUNT = 10;
private static final int ORIGINATE_TIME_OFFSET = 24;
private static final int RECEIVE_TIME_OFFSET = 32;
private static final int TRANSMIT_TIME_OFFSET = 40;
@ -191,14 +195,18 @@ public final class SntpClient {
}
private static long loadNtpTimeOffsetMs() throws IOException {
InetAddress address = InetAddress.getByName(getNtpHost());
try (DatagramSocket socket = new DatagramSocket()) {
socket.setSoTimeout(getTimeoutMs());
int retryCount = 0;
SocketTimeoutException timeoutException = null;
InetAddress[] addresses = InetAddress.getAllByName(getNtpHost());
for (InetAddress address : addresses) {
byte[] buffer = new byte[NTP_PACKET_SIZE];
DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, NTP_PORT);
// Set mode = 3 (client) and version = 3. Mode is in low 3 bits of the first byte and Version
// is in bits 3-5 of the first byte.
// Set mode = 3 (client) and version = 3. Mode is in low 3 bits of the first byte and
// Version is in bits 3-5 of the first byte.
buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);
// Get current time and write it to the request packet.
@ -210,7 +218,22 @@ public final class SntpClient {
// Read the response.
DatagramPacket response = new DatagramPacket(buffer, buffer.length);
try {
socket.receive(response);
} catch (SocketTimeoutException e) {
// Store the timeout exception and try the next address if we have not reached retry limit
if (timeoutException == null) {
timeoutException = e;
} else {
timeoutException.addSuppressed(e);
}
if (retryCount++ < MAX_RETRY_COUNT) {
continue;
} else {
break;
}
}
final long responseTicks = SystemClock.elapsedRealtime();
final long responseTime = requestTime + (responseTicks - requestTicks);
@ -235,13 +258,16 @@ public final class SntpClient {
// = (2 * skew)/2 = skew
long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime)) / 2;
// Save our results using the times on this side of the network latency (i.e. response rather
// than request time)
// Save our results using the times on this side of the network latency (i.e. response
// rather than request time)
long ntpTime = responseTime + clockOffset;
long ntpTimeReference = responseTicks;
return ntpTime - ntpTimeReference;
}
// If no response is received from any of the addresses, throw an exception.
throw checkNotNull(timeoutException);
}
}
private static long readTimestamp(byte[] buffer, int offset) {