diff --git a/app/src/main/java/info/nightscout/androidaps/utils/SntpClient.java b/app/src/main/java/info/nightscout/androidaps/utils/SntpClient.java deleted file mode 100644 index 0bfa1cbd0d..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/utils/SntpClient.java +++ /dev/null @@ -1,248 +0,0 @@ -package info.nightscout.androidaps.utils; -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import android.os.SystemClock; - -import org.slf4j.Logger; - -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.logging.StacktraceLoggerWrapper; - -/** - * {@hide} - *
- * Simple SNTP client class for retrieving network time. - *
- * Sample usage: - *
SntpClient client = new SntpClient(); - * if (client.requestTime("time.foo.com")) { - * long now = client.getNtpTime() + SystemClock.elapsedRealtime() - client.getNtpTimeReference(); - * } - *- */ -@Singleton -public class SntpClient { - private final AAPSLogger aapsLogger; - private final DateUtil dateUtil; - - //private final int REFERENCE_TIME_OFFSET = 16; - private final int ORIGINATE_TIME_OFFSET = 24; - private final int RECEIVE_TIME_OFFSET = 32; - private final int TRANSMIT_TIME_OFFSET = 40; - private final int NTP_PACKET_SIZE = 48; - - private final int NTP_PORT = 123; - private final int NTP_MODE_CLIENT = 3; - private final int NTP_VERSION = 3; - - @Inject - public SntpClient( - AAPSLogger aapsLogger, - DateUtil dateUtil - ) { - this.aapsLogger = aapsLogger; - this.dateUtil = dateUtil; - } - - // Number of seconds between Jan 1, 1900 and Jan 1, 1970 - // 70 years plus 17 leap days - private final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L; - - // system time computed from NTP server response - private long mNtpTime; - - // value of SystemClock.elapsedRealtime() corresponding to mNtpTime - private long mNtpTimeReference; - - // round trip time in milliseconds - private long mRoundTripTime; - - public static abstract class Callback implements Runnable { - public boolean networkConnected = false; - public boolean success = false; - public long time = 0; - } - - public synchronized void ntpTime(final Callback callback, boolean isConnected) { - callback.networkConnected = isConnected; - if (callback.networkConnected) { - new Thread(() -> doNtpTime(callback)).start(); - } else { - callback.run(); - } - } - - void doNtpTime(final Callback callback) { - aapsLogger.debug("Time detection started"); - callback.success = requestTime("time.google.com", 5000); - callback.time = getNtpTime() + SystemClock.elapsedRealtime() - getNtpTimeReference(); - aapsLogger.debug("Time detection ended: " + callback.success + " " + dateUtil.dateAndTimeString(getNtpTime())); - callback.run(); - } - - /** - * Sends an SNTP request to the given host and processes the response. - * - * @param host host name of the server. - * @param timeout network timeout in milliseconds. - * @return true if the transaction was successful. - */ - private synchronized boolean requestTime(String host, int timeout) { - try { - DatagramSocket socket = new DatagramSocket(); - socket.setSoTimeout(timeout); - InetAddress address = InetAddress.getByName(host); - 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 first byte - // version is in bits 3-5 of first byte - buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3); - - // get current time and write it to the request packet - long requestTime = System.currentTimeMillis(); - long requestTicks = SystemClock.elapsedRealtime(); - writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime); - - socket.send(request); - - // read the response - DatagramPacket response = new DatagramPacket(buffer, buffer.length); - socket.receive(response); - long responseTicks = SystemClock.elapsedRealtime(); - long responseTime = requestTime + (responseTicks - requestTicks); - socket.close(); - - // extract the results - long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET); - long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET); - long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET); - long roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime); - // receiveTime = originateTime + transit + skew - // responseTime = transmitTime + transit - skew - // clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2 - // = ((originateTime + transit + skew - originateTime) + - // (transmitTime - (transmitTime + transit - skew)))/2 - // = ((transit + skew) + (transmitTime - transmitTime - transit + skew))/2 - // = (transit + skew - transit + skew)/2 - // = (2 * skew)/2 = skew - long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime)) / 2; - // if (Config.LOGD) Log.d(TAG, "round trip: " + roundTripTime + " ms"); - // if (Config.LOGD) Log.d(TAG, "clock offset: " + clockOffset + " ms"); - - // save our results - use the times on this side of the network latency - // (response rather than request time) - mNtpTime = responseTime + clockOffset; - mNtpTimeReference = responseTicks; - mRoundTripTime = roundTripTime; - } catch (Exception e) { - aapsLogger.debug("request time failed: " + e); - return false; - } - - return true; - } - - /** - * Returns the time computed from the NTP transaction. - * - * @return time value computed from NTP server response. - */ - private long getNtpTime() { - return mNtpTime; - } - - /** - * Returns the reference clock value (value of SystemClock.elapsedRealtime()) - * corresponding to the NTP time. - * - * @return reference clock corresponding to the NTP time. - */ - private long getNtpTimeReference() { - return mNtpTimeReference; - } - - /** - * Returns the round trip time of the NTP transaction - * - * @return round trip time in milliseconds. - */ - public long getRoundTripTime() { - return mRoundTripTime; - } - - /** - * Reads an unsigned 32 bit big endian number from the given offset in the buffer. - */ - private long read32(byte[] buffer, int offset) { - byte b0 = buffer[offset]; - byte b1 = buffer[offset + 1]; - byte b2 = buffer[offset + 2]; - byte b3 = buffer[offset + 3]; - - // convert signed bytes to unsigned values - int i0 = ((b0 & 0x80) == 0x80 ? (b0 & 0x7F) + 0x80 : b0); - int i1 = ((b1 & 0x80) == 0x80 ? (b1 & 0x7F) + 0x80 : b1); - int i2 = ((b2 & 0x80) == 0x80 ? (b2 & 0x7F) + 0x80 : b2); - int i3 = ((b3 & 0x80) == 0x80 ? (b3 & 0x7F) + 0x80 : b3); - - return ((long) i0 << 24) + ((long) i1 << 16) + ((long) i2 << 8) + (long) i3; - } - - /** - * Reads the NTP time stamp at the given offset in the buffer and returns - * it as a system time (milliseconds since January 1, 1970). - */ - private long readTimeStamp(byte[] buffer, int offset) { - long seconds = read32(buffer, offset); - long fraction = read32(buffer, offset + 4); - return ((seconds - OFFSET_1900_TO_1970) * 1000) + ((fraction * 1000L) / 0x100000000L); - } - - /** - * Writes system time (milliseconds since January 1, 1970) as an NTP time stamp - * at the given offset in the buffer. - */ - private void writeTimeStamp(byte[] buffer, int offset, long time) { - long seconds = time / 1000L; - long milliseconds = time - seconds * 1000L; - seconds += OFFSET_1900_TO_1970; - - // write seconds in big endian format - buffer[offset++] = (byte) (seconds >> 24); - buffer[offset++] = (byte) (seconds >> 16); - buffer[offset++] = (byte) (seconds >> 8); - buffer[offset++] = (byte) (seconds >> 0); - - long fraction = milliseconds * 0x100000000L / 1000L; - // write fraction in big endian format - buffer[offset++] = (byte) (fraction >> 24); - buffer[offset++] = (byte) (fraction >> 16); - buffer[offset++] = (byte) (fraction >> 8); - // low order bits should be random data - buffer[offset++] = (byte) (Math.random() * 255.0); - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/SntpClient.kt b/app/src/main/java/info/nightscout/androidaps/utils/SntpClient.kt new file mode 100644 index 0000000000..3465bb8edd --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/SntpClient.kt @@ -0,0 +1,214 @@ +package info.nightscout.androidaps.utils + +import android.os.SystemClock +import javax.inject.Singleton +import javax.inject.Inject +import info.nightscout.androidaps.logging.AAPSLogger +import java.lang.Exception +import java.net.DatagramPacket +import java.net.DatagramSocket +import java.net.InetAddress + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ /** + * {@hide} + * + * + * Simple SNTP client class for retrieving network time. + * + * + * Sample usage: + *
SntpClient client = new SntpClient(); + * if (client.requestTime("time.foo.com")) { + * long now = client.getNtpTime() + SystemClock.elapsedRealtime() - client.getNtpTimeReference(); + * } +* + */ +@Singleton +class SntpClient @Inject constructor( + private val aapsLogger: AAPSLogger, + private val dateUtil: DateUtil +) { + + companion object { + //private final int REFERENCE_TIME_OFFSET = 16; + private const val ORIGINATE_TIME_OFFSET = 24 + private const val RECEIVE_TIME_OFFSET = 32 + private const val TRANSMIT_TIME_OFFSET = 40 + private const val NTP_PACKET_SIZE = 48 + private const val NTP_PORT = 123 + private const val NTP_MODE_CLIENT = 3 + private const val NTP_VERSION = 3 + + // Number of seconds between Jan 1, 1900 and Jan 1, 1970 + // 70 years plus 17 leap days + private const val OFFSET_1900_TO_1970 = (365L * 70L + 17L) * 24L * 60L * 60L + } + + /** + * Returns the time computed from the NTP transaction. + * + * @return time value computed from NTP server response. + */ + // system time computed from NTP server response + private var ntpTime: Long = 0 + + /** + * Returns the reference clock value (value of SystemClock.elapsedRealtime()) + * corresponding to the NTP time. + * + * @return reference clock corresponding to the NTP time. + */ + // value of SystemClock.elapsedRealtime() corresponding to mNtpTime + private var ntpTimeReference: Long = 0 + + /** + * Returns the round trip time of the NTP transaction + * + * @return round trip time in milliseconds. + */ + // round trip time in milliseconds + private var roundTripTime: Long = 0 + + abstract class Callback : Runnable { + + var networkConnected = false + var success = false + var time: Long = 0 + } + + @Synchronized fun ntpTime(callback: Callback, isConnected: Boolean) { + callback.networkConnected = isConnected + if (callback.networkConnected) { + Thread { doNtpTime(callback) }.start() + } else { + callback.run() + } + } + + fun doNtpTime(callback: Callback) { + aapsLogger.debug("Time detection started") + callback.success = requestTime("time.google.com", 5000) + callback.time = ntpTime + SystemClock.elapsedRealtime() - ntpTimeReference + aapsLogger.debug("Time detection ended: " + callback.success + " " + dateUtil.dateAndTimeString(ntpTime)) + callback.run() + } + + /** + * Sends an SNTP request to the given host and processes the response. + * + * @param host host name of the server. + * @param timeout network timeout in milliseconds. + * @return true if the transaction was successful. + */ + @Suppress("SameParameterValue") + @Synchronized private fun requestTime(host: String, timeout: Int): Boolean { + try { + val socket = DatagramSocket() + socket.soTimeout = timeout + val address = InetAddress.getByName(host) + val buffer = ByteArray(NTP_PACKET_SIZE) + val request = DatagramPacket(buffer, buffer.size, address, NTP_PORT) + + // set mode = 3 (client) and version = 3 + // mode is in low 3 bits of first byte + // version is in bits 3-5 of first byte + buffer[0] = (NTP_MODE_CLIENT or (NTP_VERSION shl 3)).toByte() + + // get current time and write it to the request packet + val requestTime = System.currentTimeMillis() + val requestTicks = SystemClock.elapsedRealtime() + writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime) + socket.send(request) + + // read the response + val response = DatagramPacket(buffer, buffer.size) + socket.receive(response) + val responseTicks = SystemClock.elapsedRealtime() + val responseTime = requestTime + (responseTicks - requestTicks) + socket.close() + + // extract the results + val originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET) + val receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET) + val transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET) + val roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime) + val clockOffset = (receiveTime - originateTime + (transmitTime - responseTime)) / 2 + + // save our results - use the times on this side of the network latency + // (response rather than request time) + ntpTime = responseTime + clockOffset + ntpTimeReference = responseTicks + this.roundTripTime = roundTripTime + } catch (e: Exception) { + aapsLogger.debug("request time failed: $e") + return false + } + return true + } + + /** + * Reads an unsigned 32 bit big endian number from the given offset in the buffer. + */ + private fun read32(buffer: ByteArray, offset: Int): Long { + val b0 = buffer[offset] + val b1 = buffer[offset + 1] + val b2 = buffer[offset + 2] + val b3 = buffer[offset + 3] + + // convert signed bytes to unsigned values + val i0 = if (b0.toInt() and 0x80 == 0x80) (b0.toInt() and 0x7F) + 0x80 else b0.toInt() + val i1 = if (b1.toInt() and 0x80 == 0x80) (b1.toInt() and 0x7F) + 0x80 else b1.toInt() + val i2 = if (b2.toInt() and 0x80 == 0x80) (b2.toInt() and 0x7F) + 0x80 else b2.toInt() + val i3 = if (b3.toInt() and 0x80 == 0x80) (b3.toInt() and 0x7F) + 0x80 else b3.toInt() + return (i0.toLong() shl 24) + (i1.toLong() shl 16) + (i2.toLong() shl 8) + i3.toLong() + } + + /** + * Reads the NTP time stamp at the given offset in the buffer and returns + * it as a system time (milliseconds since January 1, 1970). + */ + private fun readTimeStamp(buffer: ByteArray, offset: Int): Long { + val seconds = read32(buffer, offset) + val fraction = read32(buffer, offset + 4) + return (seconds - OFFSET_1900_TO_1970) * 1000 + fraction * 1000L / 0x100000000L + } + + /** + * Writes system time (milliseconds since January 1, 1970) as an NTP time stamp + * at the given offset in the buffer. + */ + @Suppress("SameParameterValue") + private fun writeTimeStamp(buffer: ByteArray, offsetParam: Int, time: Long) { + var offset = offsetParam + var seconds = time / 1000L + val milliseconds = time - seconds * 1000L + seconds += OFFSET_1900_TO_1970 + + // write seconds in big endian format + buffer[offset++] = (seconds shr 24).toByte() + buffer[offset++] = (seconds shr 16).toByte() + buffer[offset++] = (seconds shr 8).toByte() + buffer[offset++] = (seconds shr 0).toByte() + val fraction = milliseconds * 0x100000000L / 1000L + // write fraction in big endian format + buffer[offset++] = (fraction shr 24).toByte() + buffer[offset++] = (fraction shr 16).toByte() + buffer[offset++] = (fraction shr 8).toByte() + // low order bits should be random data + buffer[offset] = (Math.random() * 255.0).toInt().toByte() + } +} \ No newline at end of file