From 7af6f24dd6321f310957316d755f08bbba5f1473 Mon Sep 17 00:00:00 2001 From: Jorge Antonio Diaz-Benito Soriano Date: Thu, 3 Aug 2023 17:47:10 +0200 Subject: [PATCH] Add initial API --- library/build.gradle.kts | 16 ++++++ .../kotlin/com/tidal/networktime/NTPServer.kt | 41 ++++++++++++++ .../com/tidal/networktime/SNTPClient.kt | 54 +++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 library/src/commonMain/kotlin/com/tidal/networktime/NTPServer.kt create mode 100644 library/src/commonMain/kotlin/com/tidal/networktime/SNTPClient.kt diff --git a/library/build.gradle.kts b/library/build.gradle.kts index 0e8d4123..f40686bb 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -31,4 +31,20 @@ kotlin { androidNativeX64() mingwX64() watchosDeviceArm64() + + sourceSets { + val commonMain by getting { + dependencies { + api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") + } + } + } +} + +android { + compileSdk = 34 + defaultConfig { + minSdk = 1 + } + namespace = group.toString() } diff --git a/library/src/commonMain/kotlin/com/tidal/networktime/NTPServer.kt b/library/src/commonMain/kotlin/com/tidal/networktime/NTPServer.kt new file mode 100644 index 00000000..fda31b34 --- /dev/null +++ b/library/src/commonMain/kotlin/com/tidal/networktime/NTPServer.kt @@ -0,0 +1,41 @@ +package com.tidal.networktime + +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds + +sealed interface NTPServer { + val hostName: String + val responseTimeout: Duration + val dnsResolutionStrategy: DNSResolutionStrategy + + sealed interface Unicast : NTPServer { + class Sequential( + override val hostName: String, + override val responseTimeout: Duration = 5.seconds, + override val dnsResolutionStrategy: DNSResolutionStrategy = DNSResolutionStrategy.ALL, + val outgoingRequestGap: Duration = OUTGOING_REQUEST_GAP_DEFAULT_SECONDS.seconds, + ) : Unicast + + class Concurrent( + override val hostName: String, + override val responseTimeout: Duration = 5.seconds, + override val dnsResolutionStrategy: DNSResolutionStrategy = DNSResolutionStrategy.ALL, + ) : Unicast + + companion object { + private const val OUTGOING_REQUEST_GAP_DEFAULT_SECONDS = 2 + } + } + + class Anycast( + override val hostName: String, + override val responseTimeout: Duration = 68.seconds, + override val dnsResolutionStrategy: DNSResolutionStrategy = DNSResolutionStrategy.ALL, + ) : NTPServer + + enum class DNSResolutionStrategy { + IP_V4, + IP_V6, + ALL, + } +} diff --git a/library/src/commonMain/kotlin/com/tidal/networktime/SNTPClient.kt b/library/src/commonMain/kotlin/com/tidal/networktime/SNTPClient.kt new file mode 100644 index 00000000..27cb7b33 --- /dev/null +++ b/library/src/commonMain/kotlin/com/tidal/networktime/SNTPClient.kt @@ -0,0 +1,54 @@ +package com.tidal.networktime + +import kotlinx.coroutines.CoroutineScope +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds + +/** + * Construct a new SNTP client that can be requested to periodically interact with the provided + * [ntpServers] to obtain information about their provided time. + * + * @param ntpServers Representation of supported NTP sources. + * @param coroutineScope The scope where synchronization will run on. + * @param ntpVersion The version number to write in packets. + * @param portSelectionStrategy The strategy for selecting a port to operate on. + * @param minimumSynchronizationInterval The minimum amount of time between performing time queries + * on all unicast sources. The actual value used may be larger than this on occasion based on + * changes to the difference between the time provided by [ntpServers] and [referenceClock] (if it + * ever changes), but will never be lower than this value. + * @param referenceClock A clock used to calculate timing differences with the information obtained + * from [ntpServers]. This clock will never be modified directly. + */ +class SNTPClient( + vararg val ntpServers: NTPServer, + val coroutineScope: CoroutineScope, + val ntpVersion: NTPVersion = NTPVersion.FOUR, + val portSelectionStrategy: PortSelectionStrategy = PortSelectionStrategy.RFC9109, + val minimumSynchronizationInterval: Duration = 64.seconds, + val referenceClock: () -> Long, +) { + + val time: Long? + get() = TODO("Getting the time") + + fun startSynchronization(): Unit = TODO("Start or return") + + fun stopSynchronization(): Unit = TODO("Stop or return") + + enum class NTPVersion(val descriptor: Short) { + ZERO(0), + ONE(1), + TWO(2), + THREE(3), + FOUR(4), + } + + sealed class PortSelectionStrategy(val pinnedPortNumber: Int?) { + data object RFC5905 : PortSelectionStrategy(123) + + /** + * Make a new selection of a random available port every time a socket is opened. + */ + data object RFC9109 : PortSelectionStrategy(null) + } +}