1 /*
2  * Copyright (C) 2022 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.app.time.cts;
18 
19 import android.util.Log;
20 
21 import java.io.IOException;
22 import java.net.DatagramPacket;
23 import java.net.DatagramSocket;
24 import java.net.InetAddress;
25 import java.net.SocketException;
26 import java.util.Arrays;
27 
28 /**
29  * Simple Sntp Server implementation for testing purpose.
30  * This is copied from {@code SntpClientTest}.
31  */
32 class SntpTestServer {
33     private static final String TAG = SntpTestServer.class.getSimpleName();
34     private static final int ORIGINATE_TIME_OFFSET = 24;
35     private static final int TRANSMIT_TIME_OFFSET = 40;
36 
37     private final Object mLock = new Object();
38     private final DatagramSocket mSocket;
39     private final InetAddress mAddress;
40     private final int mPort;
41     private byte[] mReply;
42     private boolean mGenerateValidOriginateTimestamp = true;
43     private int mRcvd;
44     private int mSent;
45     private final Thread mListeningThread;
46 
SntpTestServer()47     SntpTestServer() {
48         mSocket = makeSocket();
49         mAddress = mSocket.getLocalAddress();
50         mPort = mSocket.getLocalPort();
51         Log.d(TAG, "testing server listening on (" + mAddress + ", " + mPort + ")");
52 
53         mListeningThread = new Thread() {
54             public void run() {
55                 while (true) {
56                     byte[] buffer = new byte[512];
57                     DatagramPacket ntpMsg = new DatagramPacket(buffer, buffer.length);
58                     try {
59                         mSocket.receive(ntpMsg);
60                     } catch (IOException e) {
61                         Log.e(TAG, "datagram receive error: " + e);
62                         break;
63                     }
64                     synchronized (mLock) {
65                         mRcvd++;
66                         if (mReply == null) {
67                             continue;
68                         }
69                         if (mGenerateValidOriginateTimestamp) {
70                             // Copy the transmit timestamp into originate timestamp: This is
71                             // validated by well-behaved clients.
72                             System.arraycopy(ntpMsg.getData(), TRANSMIT_TIME_OFFSET,
73                                     mReply, ORIGINATE_TIME_OFFSET, 8);
74                         } else {
75                             // Fill it with junk instead.
76                             Arrays.fill(mReply, ORIGINATE_TIME_OFFSET,
77                                     ORIGINATE_TIME_OFFSET + 8, (byte) 0xFF);
78                         }
79                         ntpMsg.setData(mReply);
80                         ntpMsg.setLength(mReply.length);
81                         try {
82                             mSocket.send(ntpMsg);
83                         } catch (IOException e) {
84                             Log.e(TAG, "datagram send error: " + e);
85                             break;
86                         }
87                         mSent++;
88                     }
89                 }
90                 mSocket.close();
91             }
92         };
93         mListeningThread.start();
94     }
95 
makeSocket()96     private DatagramSocket makeSocket() {
97         DatagramSocket socket;
98         try {
99             socket = new DatagramSocket(0, InetAddress.getLoopbackAddress());
100         } catch (SocketException e) {
101             Log.e(TAG, "Failed to create test server socket: " + e);
102             throw new RuntimeException("Failed to create test server socket", e);
103         }
104         return socket;
105     }
106 
clearServerReply()107     public void clearServerReply() {
108         setServerReply(null);
109     }
110 
setServerReply(byte[] reply)111     public void setServerReply(byte[] reply) {
112         synchronized (mLock) {
113             mReply = reply;
114             mRcvd = 0;
115             mSent = 0;
116         }
117     }
118 
119     /**
120      * Controls the test server's behavior of copying the client's transmit timestamp into the
121      * response's originate timestamp (which is required of a real server).
122      */
setGenerateValidOriginateTimestamp(boolean enabled)123     public void setGenerateValidOriginateTimestamp(boolean enabled) {
124         synchronized (mLock) {
125             mGenerateValidOriginateTimestamp = enabled;
126         }
127     }
128 
getAddress()129     public InetAddress getAddress() {
130         return mAddress;
131     }
132 
getPort()133     public int getPort() {
134         return mPort;
135     }
136 
numRequestsReceived()137     public int numRequestsReceived() {
138         synchronized (mLock) {
139             return mRcvd;
140         }
141     }
142 
numRepliesSent()143     public int numRepliesSent() {
144         synchronized (mLock) {
145             return mSent;
146         }
147     }
148 }
149