Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
f3a0f45
Convert sample to kt + androidx
Jawnnypoo Apr 12, 2019
6b77436
Convert rx class to kt
Jawnnypoo Apr 12, 2019
e28e515
Convert main library module to kt
Jawnnypoo Apr 12, 2019
9837d29
Binary compatibility cleanup
Jawnnypoo Apr 12, 2019
2e5b035
Java compat
Jawnnypoo Apr 12, 2019
b674f8e
Remove Collections references
Jawnnypoo Apr 12, 2019
cab1e02
Neat app icon
Jawnnypoo Apr 12, 2019
eee0134
Add way to view base implementation from Rx implementation
Jawnnypoo Apr 12, 2019
a560af3
chore: upgrade kotlin + gradle versions
kaushikgopal Nov 25, 2020
12c0457
dep: add coroutines
kaushikgopal Nov 25, 2020
59d2d9d
skip build config generation
kaushikgopal Nov 25, 2020
43b3525
fix: update non Rx activity title
kaushikgopal Nov 25, 2020
5a43240
add TrueTime2 interface
kaushikgopal Nov 25, 2020
843f9cc
simplify Rx in TrueTimeRx
kaushikgopal Nov 25, 2020
d330482
wip: write out algorithm for coroutine implementation
kaushikgopal Nov 25, 2020
28d0f47
wip: ntp algo with todo methods
kaushikgopal Nov 25, 2020
7e9683c
reogranize sntip client package
kaushikgopal Nov 25, 2020
9cf3f45
inject sntp client into TrueTime
kaushikgopal Nov 25, 2020
90ad99a
implementation logic
kaushikgopal Nov 25, 2020
fcd3857
feat: flush out implementation for TrueTimeImpl
kaushikgopal Nov 26, 2020
94b15cc
test figures for TrueTimeImpl
kaushikgopal Nov 26, 2020
de1045d
hack to test coroutines TT
kaushikgopal Nov 26, 2020
26a3260
app example : initialize TrueTime2
kaushikgopal Nov 26, 2020
2b8cd73
fix: revert TrueTime.kt -> .java
kaushikgopal Nov 26, 2020
dda83ef
ref: reorganize packages
kaushikgopal Nov 26, 2020
be815e1
--wip: sntp duplicate
kaushikgopal Nov 26, 2020
38449d4
upgrade kotlin + coroutines gradle
kaushikgopal Dec 28, 2020
43fab29
ref: move to legacy package
kaushikgopal Dec 29, 2020
6d19a1b
feat: Sntp add devicetime api
kaushikgopal Dec 29, 2020
6036da1
feat: TrueTime2 add more usable apis
kaushikgopal Dec 29, 2020
13342c3
feat: cleanup sample app
kaushikgopal Dec 29, 2020
c60ccaa
feat: add repeat count parameter
kaushikgopal Dec 29, 2020
ef8c0fe
fix: add equals + hashcode for TruetimeResult
kaushikgopal Dec 29, 2020
9243133
store and reuse ntpResult
kaushikgopal Dec 29, 2020
053875c
ref: extract TimeKeeper logic
kaushikgopal Dec 29, 2020
7e32a99
fix: cleanup SntpImpl
kaushikgopal Dec 29, 2020
0c028a6
feat: add logger interface
kaushikgopal Dec 29, 2020
dd37470
fix: logging levels
kaushikgopal Dec 30, 2020
61a307a
ref: remove TrueTime dependencies from Sntp
kaushikgopal Dec 30, 2020
e78471e
feat: add sync through coroutines
kaushikgopal Dec 31, 2020
0a3c94c
ref: clean up api given sync present
kaushikgopal Dec 31, 2020
f1c9150
fix: truetime failure shouldn't crash
kaushikgopal Dec 31, 2020
c78f9eb
wip
kaushikgopal Nov 1, 2021
b18f9ea
package rename
kaushikgopal Aug 14, 2022
742506c
api changes
kaushikgopal Aug 14, 2022
a33593f
wip: upgrade gradle plugin
kaushikgopal Aug 14, 2022
bedee05
refactor(sntp): move from logger to event listener
kaushikgopal Aug 14, 2022
567af93
SntpImpl: move from logger → EventListener
kaushikgopal Aug 23, 2022
04dcfa2
move from logging → event listener
kaushikgopal Aug 23, 2022
2f0c346
ref: move TrueTimeParameters from data class → Builder pattern
kaushikgopal Aug 24, 2022
e5517b6
ref: remove initialize method; sync is the way to go
kaushikgopal Aug 24, 2022
a515b93
ref: event listener + sample app log listener
kaushikgopal Aug 24, 2022
051b53e
fix app based on changes
kaushikgopal Aug 24, 2022
7ec3ade
app tweaks
kaushikgopal Aug 24, 2022
0900ee6
update README
kaushikgopal Aug 24, 2022
6cd1516
update README
kaushikgopal Aug 24, 2022
03e24b5
update REAME
kaushikgopal Aug 24, 2022
b0f16e6
update REAME
kaushikgopal Aug 24, 2022
44b5772
update REAME
kaushikgopal Aug 24, 2022
fe1be4a
cleanup
kaushikgopal Aug 24, 2022
51767b9
prepare for release
kaushikgopal Aug 24, 2022
6b452d3
update jitpack instructions
kaushikgopal Aug 24, 2022
80f0a4d
fix: compileSdkVersion to 33
kaushikgopal Aug 24, 2022
dd1207a
fix: compiler warning for resolveNtpHostToIPs
kaushikgopal Aug 24, 2022
bd113cd
fix: sample app using actual truetime
kaushikgopal Aug 24, 2022
abde7e2
ref: move from String → InetAddress object
kaushikgopal Aug 24, 2022
8a0a04b
ref: change SNTP call to address vs ntphost
kaushikgopal Aug 25, 2022
8c7dcf5
feat: keep device on when using TrueTime app
kaushikgopal Aug 25, 2022
3a07875
feat: add feature to filter ipv6 addresses
kaushikgopal Aug 25, 2022
0d13df9
feat: add strict mode flag
kaushikgopal Aug 25, 2022
369e428
fix: sample app api
kaushikgopal Aug 25, 2022
5b7d498
ref: improve TrueTime Parameters naming
kaushikgopal Aug 25, 2022
e1145a6
update gitignore
kaushikgopal Feb 25, 2023
b08dc3c
rename serverResponseDelayMax → InMillis
kaushikgopal Feb 25, 2023
296ef71
improve docs
kaushikgopal Feb 25, 2023
f9174e7
use lifecyclescope in sample app
kaushikgopal Feb 25, 2023
aaadff8
fix glaring documentation bug on strictNtpMode
kaushikgopal Feb 25, 2023
72e790d
dispose activity syncer
kaushikgopal Feb 25, 2023
fe2f761
upgrade gradle
kaushikgopal Feb 25, 2023
4b115c8
reorder todos based on priority
kaushikgopal Feb 25, 2023
1b44fed
upgrade kotlin 1.8.0 coroutines 1.6.4
kaushikgopal Feb 25, 2023
3e581be
increase server response delay
kaushikgopal Feb 25, 2023
024cac8
switching to time.apple for example
kaushikgopal Feb 25, 2023
d7a1490
truetime will one day be pure kotlin, so this is pointless
kaushikgopal Feb 25, 2023
c1a5d38
agp 8 warning
kaushikgopal Feb 25, 2023
464d93d
target sdk 33 + move sample app out of instacart.library package
kaushikgopal Feb 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
move from logging → event listener
  • Loading branch information
kaushikgopal committed Aug 23, 2022
commit 04dcfa20bd0e88ca5d76b66b00005224b6342d3d
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class SampleActivity : AppCompatActivity() {
val mainDispatcherScope = CoroutineScope(Dispatchers.Main.immediate)

if (!::trueTime.isInitialized) {
trueTime = TrueTimeImpl(logger = AndroidLogger)
trueTime = TrueTimeImpl()
}

val with = TrueTimeParameters(
Expand Down
14 changes: 0 additions & 14 deletions library/src/main/java/com/instacart/truetime/EventListener.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.instacart.truetime

import com.instacart.truetime.time.TrueTimeParameters
import java.util.*

interface TrueTimeEventListener : SntpEventListener, TimeKeeperListener {

/**
* TrueTime initialize call performed
*/
fun initialize(with: TrueTimeParameters)

/**
* TrueTime came back with [ntpResult]
*/
fun initializeSuccess(ntpResult: LongArray)

/**
* Main TrueTime initialization call failed
* with a generic exception [e]
*/
fun initializeFailed(e: Exception)

/**
* The next TrueTime initialization call
* will be attempted in [delay]
*/
fun nextInitializeIn(delay: Long)

/**
* Resolved NTP pool host address [ntpHost]
* to the list of IP addresses [ipList]
*/
fun resolvedNtpHostToIPs(ntpHost: String, ipList: List<String>)

fun sntpRequestLastAttempt(ipHost: String)

/**
* SNTP request failed with exception [e]
*/
fun sntpRequestFailed(e: Exception)
}

interface SntpEventListener {

/**
* requesting time from [ntpHost] which should be an IP address
*/
fun requestingTime(ntpHost: String)

/**
* Invoked after the SNTP request to [ntpHost] comes back successful
*/
fun responseSuccessful(ntpHost: String)

/**
* Invoked if the SNTP request to [ntpHost] fails for any reason
*/
fun responseFailed(ntpHost: String, e: Exception)
}

interface TimeKeeperListener {
fun storingTrueTime(ntpResult: LongArray)

/**
* TimeKeeper has the "true" time
* and is returning it
*/
fun returningTrueTime(trueTime: Date)

/**
* TimeKeeper does not have the "true" time
* returning device time.
*/
fun returningDeviceTime()
}

object NoOpEventListener : TrueTimeEventListener {
override fun initialize(with: TrueTimeParameters) {}
override fun initializeSuccess(ntpResult: LongArray) {}
override fun initializeFailed(e: Exception) {}
override fun nextInitializeIn(delay: Long) {}
override fun resolvedNtpHostToIPs(ntpHost: String, ipList: List<String>) {}
override fun sntpRequestLastAttempt(ipHost: String) {}
override fun sntpRequestFailed(e: Exception) {}

override fun requestingTime(ntpHost: String) {}
override fun responseSuccessful(ntpHost: String) {}
override fun responseFailed(ntpHost: String, e: Exception) {}

override fun storingTrueTime(ntpResult: LongArray) {}
override fun returningTrueTime(trueTime: Date) {}
override fun returningDeviceTime() {}
}
13 changes: 7 additions & 6 deletions library/src/main/java/com/instacart/truetime/sntp/Sntp.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.instacart.truetime.sntp

import com.instacart.truetime.SntpEventListener
import java.io.IOException
import java.lang.Exception

Expand Down Expand Up @@ -34,12 +35,12 @@ interface Sntp {
*/
@Throws(IOException::class)
fun requestTime(
ntpHostAddress: String,
rootDelayMax: Float,
rootDispersionMax: Float,
serverResponseDelayMax: Int,
timeoutInMillis: Int,
listener: SntpEventListener,
ntpHostAddress: String,
rootDelayMax: Float,
rootDispersionMax: Float,
serverResponseDelayMax: Int,
timeoutInMillis: Int,
listener: SntpEventListener,
): LongArray

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

import com.instacart.truetime.SntpEventListener;
import org.jetbrains.annotations.NotNull;

/**
Expand Down Expand Up @@ -100,6 +102,8 @@ public synchronized long[] requestTime(
SntpEventListener listener
) throws IOException {

listener.requestingTime(ntpHost);

DatagramSocket socket = null;

try {
Expand Down
13 changes: 10 additions & 3 deletions library/src/main/java/com/instacart/truetime/time/TimeKeeper.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.instacart.truetime.time

import android.os.SystemClock
import com.instacart.truetime.TimeKeeperListener
import com.instacart.truetime.sntp.Sntp
import java.util.Date
import java.util.concurrent.atomic.AtomicReference
Expand All @@ -12,20 +13,26 @@ import java.util.concurrent.atomic.AtomicReference
* and derives true time from that result
*/
internal class TimeKeeper(
private val sntp: Sntp
private val sntp: Sntp,
private val listener: TimeKeeperListener,
) {
private var ttResult: AtomicReference<LongArray> = AtomicReference()

fun hasTheTime(): Boolean = ttResult.get() != null

fun save(ntpResult: LongArray) = ttResult.set(ntpResult)
fun save(ntpResult: LongArray) {
listener.storingTrueTime(ntpResult)
ttResult.set(ntpResult)
}

fun now(): Date {
val ntpResult = ttResult.get()
val savedSntpTime: Long = sntp.trueTime(ntpResult)
val timeSinceBoot: Long = sntp.timeSinceBoot(ntpResult)
val currentTimeSinceBoot: Long = SystemClock.elapsedRealtime()
val trueTime = Date(savedSntpTime + (currentTimeSinceBoot - timeSinceBoot))

return Date(savedSntpTime + (currentTimeSinceBoot - timeSinceBoot))
listener.returningTrueTime(trueTime)
return trueTime
}
}
60 changes: 24 additions & 36 deletions library/src/main/java/com/instacart/truetime/time/TrueTimeImpl.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package com.instacart.truetime.time

import com.instacart.truetime.EventListener
import com.instacart.truetime.TrueTimeEventListener
import com.instacart.truetime.NoOpEventListener
import com.instacart.truetime.log.Logger
import com.instacart.truetime.log.LoggerNoOp
import com.instacart.truetime.sntp.Sntp
import com.instacart.truetime.sntp.SntpEventListener
import com.instacart.truetime.sntp.SntpImpl
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
Expand All @@ -17,16 +14,11 @@ import java.net.UnknownHostException
import java.util.Date

class TrueTimeImpl(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is the one people will use, I think it might be clearer to name this one TrueTime and name the interface TrueTimeBase or something similar, so that TrueTime is the more discoverable one

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm... i prefer having TrueTime be the interface mostly cause that's what people will be inclined to use and pass around more. It being an interface means they will use the interface in most places which is what we want 🙂. TrueTimeImpl should probably just be used in one place and forgotten.

private val eventListener: EventListener = NoOpEventListener,
private val logger: Logger = LoggerNoOp,
private val listener: TrueTimeEventListener = NoOpEventListener,
) : TrueTime {

private val sntp: Sntp = SntpImpl()
private val timeKeeper = TimeKeeper(sntp)

companion object {
private const val TAG: String = "TrueTimeImpl"
}
private val timeKeeper = TimeKeeper(sntp, listener)

override fun initialized(): Boolean = timeKeeper.hasTheTime()

Expand All @@ -36,55 +28,50 @@ class TrueTimeImpl(
try {
initialize(with)
} catch (e: Exception) {
logger.e(TAG, "- TrueTime initialization failed", e)
listener.initializeFailed(e)
}
delay(with.syncIntervalInMillis)
logger.v(TAG, "- starting next resync")

listener.nextInitializeIn(with.syncIntervalInMillis)
delay(with.syncIntervalInMillis)
}
}
}

override fun initialize(with: TrueTimeParameters): Date {
logger.v(TAG, "- initializing TrueTime")
val ntpResult = init(with)
logger.v(TAG, "- saving TrueTime NTP result")
timeKeeper.save(ntpResult = ntpResult)
logger.v(TAG, "- returning Time now")
return timeKeeper.now()
}

override fun nowSafely(): Date {
return if (timeKeeper.hasTheTime()) {
logger.v(TAG, "TimeKeeper has the time")
nowTrueOnly()
} else {
logger.d(TAG, "TimeKeeper does NOT have time: returning device time safely")
listener.returningDeviceTime()
Date()
}
}

override fun nowTrueOnly(): Date {
if (!initialized()) throw IllegalStateException("TrueTime was not initialized successfully yet")
logger.v(TAG, "returning Time now")
return timeKeeper.now()
}


//region private helpers

/**
* Initialize TrueTime with an ntp pool server address
*/
private fun init(with: TrueTimeParameters): LongArray {
listener.initialize(with)

// resolve NTP pool -> single IPs
return resolveNtpHostToIPs(with.ntpHostPool.first())
val ntpResult = resolveNtpHostToIPs(with.ntpHostPool.first())
// for each IP resolved
.map { ipHost ->
logger.v(TAG, "---- requesting time (single IP: $ipHost)")
// 5 times against each IP
(1..5)
.map { requestTime(with, ipHost, eventListener) }
.map { requestTime(with, ipHost) }
// collect the 5 results to list
.toList()
// filter least round trip delay to get single Result
Expand All @@ -94,49 +81,50 @@ class TrueTimeImpl(
.take(5)
// filter median clock offset to get single Result
.filterMedianClockOffset()

listener.initializeSuccess(ntpResult)
return ntpResult
}

/**
* resolve ntp host pool address to single IPs
*/
@Throws(UnknownHostException::class)
private fun resolveNtpHostToIPs(ntpHost: String): List<String> {
logger.v(TAG, "-- resolving ntpHost : $ntpHost")
return InetAddress.getAllByName(ntpHost).map { it.hostAddress }
private fun resolveNtpHostToIPs(ntpHostAddress: String): List<String> {
val ipList = InetAddress.getAllByName(ntpHostAddress).map { it.hostAddress }
listener.resolvedNtpHostToIPs(ntpHostAddress, ipList)
return ipList
}

private fun requestTime(
with: TrueTimeParameters,
ipHostAddress: String,
eventListener: EventListener,
): LongArray {
// retrying upto (default 50) times if necessary
repeat(with.retryCountAgainstSingleIp - 1) {
try {
// request Time
logger.v(TAG, "------ requesting SNTP time")
return sntpRequest(with, ipHostAddress, eventListener)
return sntpRequest(with, ipHostAddress)
} catch (e: Exception) {
logger.e(TAG, "------ Error requesting SNTP time", e)
listener.sntpRequestFailed(e)
}
}

// last attempt
logger.i(TAG, "---- last attempt for $ipHostAddress")
listener.sntpRequestLastAttempt(ipHostAddress)
return sntpRequest(with, ipHostAddress)
}

private fun sntpRequest(
with: TrueTimeParameters,
ipHostAddress: String,
eventListener: EventListener,
with: TrueTimeParameters,
ipHostAddress: String,
): LongArray = sntp.requestTime(
ntpHostAddress = ipHostAddress,
rootDelayMax = with.rootDelayMax,
rootDispersionMax = with.rootDispersionMax,
serverResponseDelayMax = with.serverResponseDelayMax,
timeoutInMillis = with.connectionTimeoutInMillis,
listener = eventListener as SntpEventListener
listener = listener,
)

private fun List<LongArray>.filterLeastRoundTripDelay(): LongArray {
Expand Down