blob: 97afa60f0c6d8db999f94980d5d290e1fb185a8f [file] [log] [blame]
Hugo Benichi5e055182016-06-01 08:50:38 +09001/*
2 * Copyright (C) 2016, 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
17package com.android.server.connectivity;
18
Hugo Benichi5e055182016-06-01 08:50:38 +090019import android.net.ConnectivityManager;
Hugo Benichi2299a1c2016-11-25 11:24:22 +090020import android.net.ConnectivityManager.NetworkCallback;
Hugo Benichi5e055182016-06-01 08:50:38 +090021import android.net.Network;
22import android.net.metrics.DnsEvent;
Michal Karpinski7f9014b2016-11-21 14:58:25 +000023import android.net.metrics.INetdEventListener;
Hugo Benichi5e055182016-06-01 08:50:38 +090024import android.net.metrics.IpConnectivityLog;
Michal Karpinskic3abd342016-10-12 14:59:26 +010025import android.os.RemoteException;
Hugo Benichi2299a1c2016-11-25 11:24:22 +090026import android.system.OsConstants;
27import android.test.suitebuilder.annotation.SmallTest;
28import com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent;
29import java.io.FileOutputStream;
30import java.io.PrintWriter;
31import java.util.ArrayList;
32import java.util.Arrays;
33import java.util.Comparator;
34import java.util.List;
35import java.util.OptionalInt;
36import java.util.stream.IntStream;
Hugo Benichi5e055182016-06-01 08:50:38 +090037import junit.framework.TestCase;
38import org.junit.Before;
39import org.junit.Test;
Hugo Benichi5e055182016-06-01 08:50:38 +090040import org.mockito.ArgumentCaptor;
41import org.mockito.Mock;
42import org.mockito.Mockito;
43import org.mockito.MockitoAnnotations;
Hugo Benichi2299a1c2016-11-25 11:24:22 +090044
45import static org.junit.Assert.assertArrayEquals;
46import static org.junit.Assert.assertTrue;
Hugo Benichi5e055182016-06-01 08:50:38 +090047import static org.mockito.Mockito.any;
48import static org.mockito.Mockito.anyInt;
49import static org.mockito.Mockito.eq;
50import static org.mockito.Mockito.timeout;
51import static org.mockito.Mockito.times;
52import static org.mockito.Mockito.verify;
53
Michal Karpinski7f9014b2016-11-21 14:58:25 +000054public class NetdEventListenerServiceTest extends TestCase {
Hugo Benichi5e055182016-06-01 08:50:38 +090055
Michal Karpinski7f9014b2016-11-21 14:58:25 +000056 // TODO: read from NetdEventListenerService after this constant is read from system property
Hugo Benichi5e055182016-06-01 08:50:38 +090057 static final int BATCH_SIZE = 100;
Michal Karpinski7f9014b2016-11-21 14:58:25 +000058 static final int EVENT_TYPE = INetdEventListener.EVENT_GETADDRINFO;
59 // TODO: read from INetdEventListener
Hugo Benichi5e055182016-06-01 08:50:38 +090060 static final int RETURN_CODE = 1;
61
62 static final byte[] EVENT_TYPES = new byte[BATCH_SIZE];
63 static final byte[] RETURN_CODES = new byte[BATCH_SIZE];
64 static final int[] LATENCIES = new int[BATCH_SIZE];
65 static {
66 for (int i = 0; i < BATCH_SIZE; i++) {
67 EVENT_TYPES[i] = EVENT_TYPE;
68 RETURN_CODES[i] = RETURN_CODE;
69 LATENCIES[i] = i;
70 }
71 }
72
Hugo Benichi2299a1c2016-11-25 11:24:22 +090073 private static final String EXAMPLE_IPV4 = "192.0.2.1";
74 private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
75
Michal Karpinski7f9014b2016-11-21 14:58:25 +000076 NetdEventListenerService mNetdEventListenerService;
Hugo Benichi5e055182016-06-01 08:50:38 +090077
78 @Mock ConnectivityManager mCm;
79 @Mock IpConnectivityLog mLog;
80 ArgumentCaptor<NetworkCallback> mCallbackCaptor;
Hugo Benichi2299a1c2016-11-25 11:24:22 +090081 ArgumentCaptor<DnsEvent> mDnsEvCaptor;
Hugo Benichi5e055182016-06-01 08:50:38 +090082
83 public void setUp() {
84 MockitoAnnotations.initMocks(this);
85 mCallbackCaptor = ArgumentCaptor.forClass(NetworkCallback.class);
Hugo Benichi2299a1c2016-11-25 11:24:22 +090086 mDnsEvCaptor = ArgumentCaptor.forClass(DnsEvent.class);
Michal Karpinski7f9014b2016-11-21 14:58:25 +000087 mNetdEventListenerService = new NetdEventListenerService(mCm, mLog);
Hugo Benichi5e055182016-06-01 08:50:38 +090088
89 verify(mCm, times(1)).registerNetworkCallback(any(), mCallbackCaptor.capture());
90 }
91
Hugo Benichi2299a1c2016-11-25 11:24:22 +090092 @SmallTest
93 public void testOneDnsBatch() throws Exception {
Hugo Benichi5e055182016-06-01 08:50:38 +090094 log(105, LATENCIES);
95 log(106, Arrays.copyOf(LATENCIES, BATCH_SIZE - 1)); // one lookup short of a batch event
96
Hugo Benichi2299a1c2016-11-25 11:24:22 +090097 verifyLoggedDnsEvents(new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
Hugo Benichi5e055182016-06-01 08:50:38 +090098
99 log(106, Arrays.copyOfRange(LATENCIES, BATCH_SIZE - 1, BATCH_SIZE));
100
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900101 mDnsEvCaptor = ArgumentCaptor.forClass(DnsEvent.class); // reset argument captor
102 verifyLoggedDnsEvents(
Hugo Benichi5e055182016-06-01 08:50:38 +0900103 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
104 new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES));
105 }
106
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900107 @SmallTest
108 public void testSeveralDmsBatches() throws Exception {
Hugo Benichi5e055182016-06-01 08:50:38 +0900109 log(105, LATENCIES);
110 log(106, LATENCIES);
111 log(105, LATENCIES);
112 log(107, LATENCIES);
113
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900114 verifyLoggedDnsEvents(
Hugo Benichi5e055182016-06-01 08:50:38 +0900115 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
116 new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES),
117 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
118 new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES));
119 }
120
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900121 @SmallTest
122 public void testDnsBatchAndNetworkLost() throws Exception {
Hugo Benichi5e055182016-06-01 08:50:38 +0900123 byte[] eventTypes = Arrays.copyOf(EVENT_TYPES, 20);
124 byte[] returnCodes = Arrays.copyOf(RETURN_CODES, 20);
125 int[] latencies = Arrays.copyOf(LATENCIES, 20);
126
127 log(105, LATENCIES);
128 log(105, latencies);
129 mCallbackCaptor.getValue().onLost(new Network(105));
130 log(105, LATENCIES);
131
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900132 verifyLoggedDnsEvents(
Hugo Benichi5e055182016-06-01 08:50:38 +0900133 new DnsEvent(105, eventTypes, returnCodes, latencies),
134 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
135 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
136 }
137
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900138 @SmallTest
139 public void testConcurrentDnsBatchesAndDumps() throws Exception {
Hugo Benichi5e055182016-06-01 08:50:38 +0900140 final long stop = System.currentTimeMillis() + 100;
141 final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
142 new Thread() {
143 public void run() {
144 while (System.currentTimeMillis() < stop) {
Michal Karpinski7f9014b2016-11-21 14:58:25 +0000145 mNetdEventListenerService.dump(pw);
Hugo Benichi5e055182016-06-01 08:50:38 +0900146 }
147 }
148 }.start();
149
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900150 logDnsAsync(105, LATENCIES);
151 logDnsAsync(106, LATENCIES);
152 logDnsAsync(107, LATENCIES);
Hugo Benichi5e055182016-06-01 08:50:38 +0900153
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900154 verifyLoggedDnsEvents(500,
Hugo Benichi5e055182016-06-01 08:50:38 +0900155 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
156 new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES),
157 new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES));
158 }
159
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900160 @SmallTest
161 public void testConcurrentDnsBatchesAndNetworkLoss() throws Exception {
162 logDnsAsync(105, LATENCIES);
Hugo Benichi5e055182016-06-01 08:50:38 +0900163 Thread.sleep(10L);
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900164 // call onLost() asynchronously to logDnsAsync's onDnsEvent() calls.
Hugo Benichi5e055182016-06-01 08:50:38 +0900165 mCallbackCaptor.getValue().onLost(new Network(105));
166
167 // do not verify unpredictable batch
168 verify(mLog, timeout(500).times(1)).log(any());
169 }
170
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900171 @SmallTest
172 public void testConnectLogging() throws Exception {
173 final int OK = 0;
174 Thread[] logActions = {
175 // ignored
176 connectEventAction(OsConstants.EALREADY, 0, EXAMPLE_IPV4),
177 connectEventAction(OsConstants.EALREADY, 0, EXAMPLE_IPV6),
178 connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4),
179 connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
180 connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
181 // valid latencies
182 connectEventAction(OK, 110, EXAMPLE_IPV4),
183 connectEventAction(OK, 23, EXAMPLE_IPV4),
184 connectEventAction(OK, 45, EXAMPLE_IPV4),
185 connectEventAction(OK, 56, EXAMPLE_IPV4),
186 connectEventAction(OK, 523, EXAMPLE_IPV6),
187 connectEventAction(OK, 214, EXAMPLE_IPV6),
188 connectEventAction(OK, 67, EXAMPLE_IPV6),
189 // errors
190 connectEventAction(OsConstants.EPERM, 0, EXAMPLE_IPV4),
191 connectEventAction(OsConstants.EPERM, 0, EXAMPLE_IPV4),
192 connectEventAction(OsConstants.EAGAIN, 0, EXAMPLE_IPV4),
193 connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV4),
194 connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV4),
195 connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV6),
196 connectEventAction(OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4),
197 connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4),
198 connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
199 connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
200 connectEventAction(OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4),
201 };
202
203 for (Thread t : logActions) {
204 t.start();
205 }
206 for (Thread t : logActions) {
207 t.join();
208 }
209
210 List<IpConnectivityEvent> events = new ArrayList<>();
211 mNetdEventListenerService.flushStatistics(events);
212
213 IpConnectivityEvent got = events.get(0);
214 String want = joinLines(
215 "time_ms: 0",
216 "transport: 0",
217 "connect_statistics <",
218 " connect_count: 12",
219 " errnos_counters <",
220 " key: 1",
221 " value: 2",
222 " >",
223 " errnos_counters <",
224 " key: 11",
225 " value: 1",
226 " >",
227 " errnos_counters <",
228 " key: 13",
229 " value: 3",
230 " >",
231 " errnos_counters <",
232 " key: 98",
233 " value: 1",
234 " >",
235 " errnos_counters <",
236 " key: 110",
237 " value: 3",
238 " >",
239 " errnos_counters <",
240 " key: 111",
241 " value: 1",
242 " >",
243 " ipv6_addr_count: 6",
244 " latencies_ms: 23",
245 " latencies_ms: 45",
246 " latencies_ms: 56",
247 " latencies_ms: 67",
248 " latencies_ms: 110",
249 " latencies_ms: 214",
250 " latencies_ms: 523");
251 verifyConnectEvent(want, got);
252 }
253
254 Thread connectEventAction(int error, int latencyMs, String ipAddr) {
255 return new Thread(() -> {
256 try {
257 mNetdEventListenerService.onConnectEvent(100, error, latencyMs, ipAddr, 80, 1);
258 } catch (Exception e) {
259 fail(e.toString());
260 }
261 });
262 }
263
Hugo Benichi5e055182016-06-01 08:50:38 +0900264 void log(int netId, int[] latencies) {
Michal Karpinskic3abd342016-10-12 14:59:26 +0100265 try {
266 for (int l : latencies) {
267 mNetdEventListenerService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l, null, null,
268 0, 0);
269 }
270 } catch (RemoteException re) {
271 throw re.rethrowFromSystemServer();
Hugo Benichi5e055182016-06-01 08:50:38 +0900272 }
273 }
274
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900275 void logDnsAsync(int netId, int[] latencies) {
Hugo Benichi5e055182016-06-01 08:50:38 +0900276 new Thread() {
277 public void run() {
278 log(netId, latencies);
279 }
280 }.start();
281 }
282
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900283 void verifyLoggedDnsEvents(DnsEvent... expected) {
284 verifyLoggedDnsEvents(0, expected);
Hugo Benichi5e055182016-06-01 08:50:38 +0900285 }
286
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900287 void verifyLoggedDnsEvents(int wait, DnsEvent... expectedEvents) {
288 verify(mLog, timeout(wait).times(expectedEvents.length)).log(mDnsEvCaptor.capture());
289 for (DnsEvent got : mDnsEvCaptor.getAllValues()) {
Hugo Benichi5e055182016-06-01 08:50:38 +0900290 OptionalInt index = IntStream.range(0, expectedEvents.length)
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900291 .filter(i -> dnsEventsEqual(expectedEvents[i], got))
Hugo Benichi5e055182016-06-01 08:50:38 +0900292 .findFirst();
293 // Don't match same expected event more than once.
294 index.ifPresent(i -> expectedEvents[i] = null);
295 assertTrue(index.isPresent());
296 }
297 }
298
299 /** equality function for DnsEvent to avoid overriding equals() and hashCode(). */
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900300 static boolean dnsEventsEqual(DnsEvent expected, DnsEvent got) {
Hugo Benichi5e055182016-06-01 08:50:38 +0900301 return (expected == got) || ((expected != null) && (got != null)
302 && (expected.netId == got.netId)
303 && Arrays.equals(expected.eventTypes, got.eventTypes)
304 && Arrays.equals(expected.returnCodes, got.returnCodes)
305 && Arrays.equals(expected.latenciesMs, got.latenciesMs));
306 }
Hugo Benichi2299a1c2016-11-25 11:24:22 +0900307
308 static String joinLines(String ... elems) {
309 StringBuilder b = new StringBuilder();
310 for (String s : elems) {
311 b.append(s).append("\n");
312 }
313 return b.toString();
314 }
315
316 static void verifyConnectEvent(String expected, IpConnectivityEvent got) {
317 try {
318 Arrays.sort(got.connectStatistics.latenciesMs);
319 Arrays.sort(got.connectStatistics.errnosCounters,
320 Comparator.comparingInt((p) -> p.key));
321 assertEquals(expected, got.toString());
322 } catch (Exception e) {
323 fail(e.toString());
324 }
325 }
Hugo Benichi5e055182016-06-01 08:50:38 +0900326}