• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.os.SystemClock;
21 import android.util.Log;
22 
23 import com.android.internal.util.TrafficStatsConstants;
24 
25 import java.net.DatagramPacket;
26 import java.net.DatagramSocket;
27 import java.net.InetAddress;
28 import java.util.Arrays;
29 
30 /**
31  * {@hide}
32  *
33  * Simple SNTP client class for retrieving network time.
34  *
35  * Sample usage:
36  * <pre>SntpClient client = new SntpClient();
37  * if (client.requestTime("time.foo.com")) {
38  *     long now = client.getNtpTime() + SystemClock.elapsedRealtime() - client.getNtpTimeReference();
39  * }
40  * </pre>
41  */
42 public class SntpClient {
43     private static final String TAG = "SntpClient";
44     private static final boolean DBG = true;
45 
46     private static final int REFERENCE_TIME_OFFSET = 16;
47     private static final int ORIGINATE_TIME_OFFSET = 24;
48     private static final int RECEIVE_TIME_OFFSET = 32;
49     private static final int TRANSMIT_TIME_OFFSET = 40;
50     private static final int NTP_PACKET_SIZE = 48;
51 
52     private static final int NTP_PORT = 123;
53     private static final int NTP_MODE_CLIENT = 3;
54     private static final int NTP_MODE_SERVER = 4;
55     private static final int NTP_MODE_BROADCAST = 5;
56     private static final int NTP_VERSION = 3;
57 
58     private static final int NTP_LEAP_NOSYNC = 3;
59     private static final int NTP_STRATUM_DEATH = 0;
60     private static final int NTP_STRATUM_MAX = 15;
61 
62     // Number of seconds between Jan 1, 1900 and Jan 1, 1970
63     // 70 years plus 17 leap days
64     private static final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L;
65 
66     // system time computed from NTP server response
67     private long mNtpTime;
68 
69     // value of SystemClock.elapsedRealtime() corresponding to mNtpTime
70     private long mNtpTimeReference;
71 
72     // round trip time in milliseconds
73     private long mRoundTripTime;
74 
75     private static class InvalidServerReplyException extends Exception {
InvalidServerReplyException(String message)76         public InvalidServerReplyException(String message) {
77             super(message);
78         }
79     }
80 
81     @UnsupportedAppUsage
SntpClient()82     public SntpClient() {
83     }
84 
85     /**
86      * Sends an SNTP request to the given host and processes the response.
87      *
88      * @param host host name of the server.
89      * @param timeout network timeout in milliseconds.
90      * @param network network over which to send the request.
91      * @return true if the transaction was successful.
92      */
requestTime(String host, int timeout, Network network)93     public boolean requestTime(String host, int timeout, Network network) {
94         final Network networkForResolv = network.getPrivateDnsBypassingCopy();
95         InetAddress address = null;
96         try {
97             address = networkForResolv.getByName(host);
98         } catch (Exception e) {
99             EventLogTags.writeNtpFailure(host, e.toString());
100             if (DBG) Log.d(TAG, "request time failed: " + e);
101             return false;
102         }
103         return requestTime(address, NTP_PORT, timeout, networkForResolv);
104     }
105 
requestTime(InetAddress address, int port, int timeout, Network network)106     public boolean requestTime(InetAddress address, int port, int timeout, Network network) {
107         DatagramSocket socket = null;
108         final int oldTag = TrafficStats.getAndSetThreadStatsTag(
109                 TrafficStatsConstants.TAG_SYSTEM_NTP);
110         try {
111             socket = new DatagramSocket();
112             network.bindSocket(socket);
113             socket.setSoTimeout(timeout);
114             byte[] buffer = new byte[NTP_PACKET_SIZE];
115             DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, port);
116 
117             // set mode = 3 (client) and version = 3
118             // mode is in low 3 bits of first byte
119             // version is in bits 3-5 of first byte
120             buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);
121 
122             // get current time and write it to the request packet
123             final long requestTime = System.currentTimeMillis();
124             final long requestTicks = SystemClock.elapsedRealtime();
125             writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime);
126 
127             socket.send(request);
128 
129             // read the response
130             DatagramPacket response = new DatagramPacket(buffer, buffer.length);
131             socket.receive(response);
132             final long responseTicks = SystemClock.elapsedRealtime();
133             final long responseTime = requestTime + (responseTicks - requestTicks);
134 
135             // extract the results
136             final byte leap = (byte) ((buffer[0] >> 6) & 0x3);
137             final byte mode = (byte) (buffer[0] & 0x7);
138             final int stratum = (int) (buffer[1] & 0xff);
139             final long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET);
140             final long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
141             final long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
142 
143             /* do sanity check according to RFC */
144             // TODO: validate originateTime == requestTime.
145             checkValidServerReply(leap, mode, stratum, transmitTime);
146 
147             long roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime);
148             // receiveTime = originateTime + transit + skew
149             // responseTime = transmitTime + transit - skew
150             // clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2
151             //             = ((originateTime + transit + skew - originateTime) +
152             //                (transmitTime - (transmitTime + transit - skew)))/2
153             //             = ((transit + skew) + (transmitTime - transmitTime - transit + skew))/2
154             //             = (transit + skew - transit + skew)/2
155             //             = (2 * skew)/2 = skew
156             long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2;
157             EventLogTags.writeNtpSuccess(address.toString(), roundTripTime, clockOffset);
158             if (DBG) {
159                 Log.d(TAG, "round trip: " + roundTripTime + "ms, " +
160                         "clock offset: " + clockOffset + "ms");
161             }
162 
163             // save our results - use the times on this side of the network latency
164             // (response rather than request time)
165             mNtpTime = responseTime + clockOffset;
166             mNtpTimeReference = responseTicks;
167             mRoundTripTime = roundTripTime;
168         } catch (Exception e) {
169             EventLogTags.writeNtpFailure(address.toString(), e.toString());
170             if (DBG) Log.d(TAG, "request time failed: " + e);
171             return false;
172         } finally {
173             if (socket != null) {
174                 socket.close();
175             }
176             TrafficStats.setThreadStatsTag(oldTag);
177         }
178 
179         return true;
180     }
181 
182     @Deprecated
183     @UnsupportedAppUsage
requestTime(String host, int timeout)184     public boolean requestTime(String host, int timeout) {
185         Log.w(TAG, "Shame on you for calling the hidden API requestTime()!");
186         return false;
187     }
188 
189     /**
190      * Returns the time computed from the NTP transaction.
191      *
192      * @return time value computed from NTP server response.
193      */
194     @UnsupportedAppUsage
getNtpTime()195     public long getNtpTime() {
196         return mNtpTime;
197     }
198 
199     /**
200      * Returns the reference clock value (value of SystemClock.elapsedRealtime())
201      * corresponding to the NTP time.
202      *
203      * @return reference clock corresponding to the NTP time.
204      */
205     @UnsupportedAppUsage
getNtpTimeReference()206     public long getNtpTimeReference() {
207         return mNtpTimeReference;
208     }
209 
210     /**
211      * Returns the round trip time of the NTP transaction
212      *
213      * @return round trip time in milliseconds.
214      */
215     @UnsupportedAppUsage
getRoundTripTime()216     public long getRoundTripTime() {
217         return mRoundTripTime;
218     }
219 
checkValidServerReply( byte leap, byte mode, int stratum, long transmitTime)220     private static void checkValidServerReply(
221             byte leap, byte mode, int stratum, long transmitTime)
222             throws InvalidServerReplyException {
223         if (leap == NTP_LEAP_NOSYNC) {
224             throw new InvalidServerReplyException("unsynchronized server");
225         }
226         if ((mode != NTP_MODE_SERVER) && (mode != NTP_MODE_BROADCAST)) {
227             throw new InvalidServerReplyException("untrusted mode: " + mode);
228         }
229         if ((stratum == NTP_STRATUM_DEATH) || (stratum > NTP_STRATUM_MAX)) {
230             throw new InvalidServerReplyException("untrusted stratum: " + stratum);
231         }
232         if (transmitTime == 0) {
233             throw new InvalidServerReplyException("zero transmitTime");
234         }
235     }
236 
237     /**
238      * Reads an unsigned 32 bit big endian number from the given offset in the buffer.
239      */
read32(byte[] buffer, int offset)240     private long read32(byte[] buffer, int offset) {
241         byte b0 = buffer[offset];
242         byte b1 = buffer[offset+1];
243         byte b2 = buffer[offset+2];
244         byte b3 = buffer[offset+3];
245 
246         // convert signed bytes to unsigned values
247         int i0 = ((b0 & 0x80) == 0x80 ? (b0 & 0x7F) + 0x80 : b0);
248         int i1 = ((b1 & 0x80) == 0x80 ? (b1 & 0x7F) + 0x80 : b1);
249         int i2 = ((b2 & 0x80) == 0x80 ? (b2 & 0x7F) + 0x80 : b2);
250         int i3 = ((b3 & 0x80) == 0x80 ? (b3 & 0x7F) + 0x80 : b3);
251 
252         return ((long)i0 << 24) + ((long)i1 << 16) + ((long)i2 << 8) + (long)i3;
253     }
254 
255     /**
256      * Reads the NTP time stamp at the given offset in the buffer and returns
257      * it as a system time (milliseconds since January 1, 1970).
258      */
readTimeStamp(byte[] buffer, int offset)259     private long readTimeStamp(byte[] buffer, int offset) {
260         long seconds = read32(buffer, offset);
261         long fraction = read32(buffer, offset + 4);
262         // Special case: zero means zero.
263         if (seconds == 0 && fraction == 0) {
264             return 0;
265         }
266         return ((seconds - OFFSET_1900_TO_1970) * 1000) + ((fraction * 1000L) / 0x100000000L);
267     }
268 
269     /**
270      * Writes system time (milliseconds since January 1, 1970) as an NTP time stamp
271      * at the given offset in the buffer.
272      */
writeTimeStamp(byte[] buffer, int offset, long time)273     private void writeTimeStamp(byte[] buffer, int offset, long time) {
274         // Special case: zero means zero.
275         if (time == 0) {
276             Arrays.fill(buffer, offset, offset + 8, (byte) 0x00);
277             return;
278         }
279 
280         long seconds = time / 1000L;
281         long milliseconds = time - seconds * 1000L;
282         seconds += OFFSET_1900_TO_1970;
283 
284         // write seconds in big endian format
285         buffer[offset++] = (byte)(seconds >> 24);
286         buffer[offset++] = (byte)(seconds >> 16);
287         buffer[offset++] = (byte)(seconds >> 8);
288         buffer[offset++] = (byte)(seconds >> 0);
289 
290         long fraction = milliseconds * 0x100000000L / 1000L;
291         // write fraction in big endian format
292         buffer[offset++] = (byte)(fraction >> 24);
293         buffer[offset++] = (byte)(fraction >> 16);
294         buffer[offset++] = (byte)(fraction >> 8);
295         // low order bits should be random data
296         buffer[offset++] = (byte)(Math.random() * 255.0);
297     }
298 }
299