blob: 75d2f1a9edfdae8e2b5fc3d0857b541a5b664807 [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 Benichi0d4a3982016-11-25 11:24:22 +090020import android.net.ConnectivityManager.NetworkCallback;
Hugo Benichi5e055182016-06-01 08:50:38 +090021import android.net.Network;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090022import android.net.metrics.ConnectStats;
Hugo Benichi5e055182016-06-01 08:50:38 +090023import android.net.metrics.DnsEvent;
Michal Karpinskia2d58602016-09-21 18:45:59 +090024import android.net.metrics.INetdEventListener;
Hugo Benichi5e055182016-06-01 08:50:38 +090025import android.net.metrics.IpConnectivityLog;
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +010026import android.os.RemoteException;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090027import android.system.OsConstants;
Hugo Benichifa8a6f62016-11-04 16:06:34 +090028import android.test.suitebuilder.annotation.SmallTest;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090029import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
30import java.io.FileOutputStream;
31import java.io.PrintWriter;
32import java.util.ArrayList;
33import java.util.Arrays;
34import java.util.Comparator;
35import java.util.List;
36import java.util.OptionalInt;
37import java.util.stream.IntStream;
Hugo Benichi5e055182016-06-01 08:50:38 +090038import junit.framework.TestCase;
39import org.junit.Before;
40import org.junit.Test;
Hugo Benichi5e055182016-06-01 08:50:38 +090041import org.mockito.ArgumentCaptor;
42import org.mockito.Mock;
43import org.mockito.Mockito;
44import org.mockito.MockitoAnnotations;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090045
46import static org.junit.Assert.assertArrayEquals;
47import static org.junit.Assert.assertTrue;
Hugo Benichi5e055182016-06-01 08:50:38 +090048import static org.mockito.Mockito.any;
49import static org.mockito.Mockito.anyInt;
50import static org.mockito.Mockito.eq;
51import static org.mockito.Mockito.timeout;
52import static org.mockito.Mockito.times;
53import static org.mockito.Mockito.verify;
54
Michal Karpinskia2d58602016-09-21 18:45:59 +090055public class NetdEventListenerServiceTest extends TestCase {
Hugo Benichi5e055182016-06-01 08:50:38 +090056
Michal Karpinskia2d58602016-09-21 18:45:59 +090057 // TODO: read from NetdEventListenerService after this constant is read from system property
Hugo Benichi5e055182016-06-01 08:50:38 +090058 static final int BATCH_SIZE = 100;
Michal Karpinskia2d58602016-09-21 18:45:59 +090059 static final int EVENT_TYPE = INetdEventListener.EVENT_GETADDRINFO;
60 // TODO: read from INetdEventListener
Hugo Benichi5e055182016-06-01 08:50:38 +090061 static final int RETURN_CODE = 1;
62
63 static final byte[] EVENT_TYPES = new byte[BATCH_SIZE];
64 static final byte[] RETURN_CODES = new byte[BATCH_SIZE];
65 static final int[] LATENCIES = new int[BATCH_SIZE];
66 static {
67 for (int i = 0; i < BATCH_SIZE; i++) {
68 EVENT_TYPES[i] = EVENT_TYPE;
69 RETURN_CODES[i] = RETURN_CODE;
70 LATENCIES[i] = i;
71 }
72 }
73
Hugo Benichi0d4a3982016-11-25 11:24:22 +090074 private static final String EXAMPLE_IPV4 = "192.0.2.1";
75 private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
76
Michal Karpinski4f261032016-09-26 09:20:25 +010077 NetdEventListenerService mNetdEventListenerService;
Hugo Benichi5e055182016-06-01 08:50:38 +090078
79 @Mock ConnectivityManager mCm;
80 @Mock IpConnectivityLog mLog;
81 ArgumentCaptor<NetworkCallback> mCallbackCaptor;
Hugo Benichi0d4a3982016-11-25 11:24:22 +090082 ArgumentCaptor<DnsEvent> mDnsEvCaptor;
Hugo Benichi5e055182016-06-01 08:50:38 +090083
84 public void setUp() {
85 MockitoAnnotations.initMocks(this);
86 mCallbackCaptor = ArgumentCaptor.forClass(NetworkCallback.class);
Hugo Benichi0d4a3982016-11-25 11:24:22 +090087 mDnsEvCaptor = ArgumentCaptor.forClass(DnsEvent.class);
Michal Karpinski4f261032016-09-26 09:20:25 +010088 mNetdEventListenerService = new NetdEventListenerService(mCm, mLog);
Hugo Benichi5e055182016-06-01 08:50:38 +090089
90 verify(mCm, times(1)).registerNetworkCallback(any(), mCallbackCaptor.capture());
91 }
92
Hugo Benichifa8a6f62016-11-04 16:06:34 +090093 @SmallTest
Hugo Benichi0d4a3982016-11-25 11:24:22 +090094 public void testOneDnsBatch() throws Exception {
Hugo Benichi5e055182016-06-01 08:50:38 +090095 log(105, LATENCIES);
96 log(106, Arrays.copyOf(LATENCIES, BATCH_SIZE - 1)); // one lookup short of a batch event
97
Hugo Benichi0d4a3982016-11-25 11:24:22 +090098 verifyLoggedDnsEvents(new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
Hugo Benichi5e055182016-06-01 08:50:38 +090099
100 log(106, Arrays.copyOfRange(LATENCIES, BATCH_SIZE - 1, BATCH_SIZE));
101
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900102 mDnsEvCaptor = ArgumentCaptor.forClass(DnsEvent.class); // reset argument captor
103 verifyLoggedDnsEvents(
Hugo Benichi5e055182016-06-01 08:50:38 +0900104 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
105 new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES));
106 }
107
Hugo Benichifa8a6f62016-11-04 16:06:34 +0900108 @SmallTest
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900109 public void testSeveralDmsBatches() throws Exception {
Hugo Benichi5e055182016-06-01 08:50:38 +0900110 log(105, LATENCIES);
111 log(106, LATENCIES);
112 log(105, LATENCIES);
113 log(107, LATENCIES);
114
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900115 verifyLoggedDnsEvents(
Hugo Benichi5e055182016-06-01 08:50:38 +0900116 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
117 new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES),
118 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
119 new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES));
120 }
121
Hugo Benichifa8a6f62016-11-04 16:06:34 +0900122 @SmallTest
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900123 public void testDnsBatchAndNetworkLost() throws Exception {
Hugo Benichi5e055182016-06-01 08:50:38 +0900124 byte[] eventTypes = Arrays.copyOf(EVENT_TYPES, 20);
125 byte[] returnCodes = Arrays.copyOf(RETURN_CODES, 20);
126 int[] latencies = Arrays.copyOf(LATENCIES, 20);
127
128 log(105, LATENCIES);
129 log(105, latencies);
130 mCallbackCaptor.getValue().onLost(new Network(105));
131 log(105, LATENCIES);
132
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900133 verifyLoggedDnsEvents(
Hugo Benichi5e055182016-06-01 08:50:38 +0900134 new DnsEvent(105, eventTypes, returnCodes, latencies),
135 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
136 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
137 }
138
Hugo Benichifa8a6f62016-11-04 16:06:34 +0900139 @SmallTest
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900140 public void testConcurrentDnsBatchesAndDumps() throws Exception {
Hugo Benichi5e055182016-06-01 08:50:38 +0900141 final long stop = System.currentTimeMillis() + 100;
142 final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
143 new Thread() {
144 public void run() {
145 while (System.currentTimeMillis() < stop) {
Michal Karpinski4f261032016-09-26 09:20:25 +0100146 mNetdEventListenerService.dump(pw);
Hugo Benichi5e055182016-06-01 08:50:38 +0900147 }
148 }
149 }.start();
150
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900151 logDnsAsync(105, LATENCIES);
152 logDnsAsync(106, LATENCIES);
153 logDnsAsync(107, LATENCIES);
Hugo Benichi5e055182016-06-01 08:50:38 +0900154
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900155 verifyLoggedDnsEvents(500,
Hugo Benichi5e055182016-06-01 08:50:38 +0900156 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
157 new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES),
158 new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES));
159 }
160
Hugo Benichifa8a6f62016-11-04 16:06:34 +0900161 @SmallTest
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900162 public void testConcurrentDnsBatchesAndNetworkLoss() throws Exception {
163 logDnsAsync(105, LATENCIES);
Hugo Benichi5e055182016-06-01 08:50:38 +0900164 Thread.sleep(10L);
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900165 // call onLost() asynchronously to logDnsAsync's onDnsEvent() calls.
Hugo Benichi5e055182016-06-01 08:50:38 +0900166 mCallbackCaptor.getValue().onLost(new Network(105));
167
168 // do not verify unpredictable batch
169 verify(mLog, timeout(500).times(1)).log(any());
170 }
171
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900172 @SmallTest
173 public void testConnectLogging() throws Exception {
174 final int OK = 0;
175 Thread[] logActions = {
176 // ignored
177 connectEventAction(OsConstants.EALREADY, 0, EXAMPLE_IPV4),
178 connectEventAction(OsConstants.EALREADY, 0, EXAMPLE_IPV6),
179 connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4),
180 connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
181 connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
182 // valid latencies
183 connectEventAction(OK, 110, EXAMPLE_IPV4),
184 connectEventAction(OK, 23, EXAMPLE_IPV4),
185 connectEventAction(OK, 45, EXAMPLE_IPV4),
186 connectEventAction(OK, 56, EXAMPLE_IPV4),
187 connectEventAction(OK, 523, EXAMPLE_IPV6),
188 connectEventAction(OK, 214, EXAMPLE_IPV6),
189 connectEventAction(OK, 67, EXAMPLE_IPV6),
190 // errors
191 connectEventAction(OsConstants.EPERM, 0, EXAMPLE_IPV4),
192 connectEventAction(OsConstants.EPERM, 0, EXAMPLE_IPV4),
193 connectEventAction(OsConstants.EAGAIN, 0, EXAMPLE_IPV4),
194 connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV4),
195 connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV4),
196 connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV6),
197 connectEventAction(OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4),
198 connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4),
199 connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
200 connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
201 connectEventAction(OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4),
202 };
203
204 for (Thread t : logActions) {
205 t.start();
206 }
207 for (Thread t : logActions) {
208 t.join();
209 }
210
211 List<IpConnectivityEvent> events = new ArrayList<>();
212 mNetdEventListenerService.flushStatistics(events);
213
214 IpConnectivityEvent got = events.get(0);
215 String want = String.join("\n",
216 "time_ms: 0",
217 "transport: 0",
218 "connect_statistics <",
219 " connect_count: 12",
220 " errnos_counters <",
221 " key: 1",
222 " value: 2",
223 " >",
224 " errnos_counters <",
225 " key: 11",
226 " value: 1",
227 " >",
228 " errnos_counters <",
229 " key: 13",
230 " value: 3",
231 " >",
232 " errnos_counters <",
233 " key: 98",
234 " value: 1",
235 " >",
236 " errnos_counters <",
237 " key: 110",
238 " value: 3",
239 " >",
240 " errnos_counters <",
241 " key: 111",
242 " value: 1",
243 " >",
244 " ipv6_addr_count: 6",
245 " latencies_ms: 23",
246 " latencies_ms: 45",
247 " latencies_ms: 56",
248 " latencies_ms: 67",
249 " latencies_ms: 110",
250 " latencies_ms: 214",
251 " latencies_ms: 523",
252 ">\n");
253 verifyConnectEvent(want, got);
254 }
255
256 Thread connectEventAction(int error, int latencyMs, String ipAddr) {
257 return new Thread(() -> {
258 try {
259 mNetdEventListenerService.onConnectEvent(100, error, latencyMs, ipAddr, 80, 1);
260 } catch (Exception e) {
261 fail(e.toString());
262 }
263 });
264 }
265
Hugo Benichi5e055182016-06-01 08:50:38 +0900266 void log(int netId, int[] latencies) {
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100267 try {
268 for (int l : latencies) {
269 mNetdEventListenerService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l, null, null,
270 0, 0);
271 }
272 } catch (RemoteException re) {
273 throw re.rethrowFromSystemServer();
Hugo Benichi5e055182016-06-01 08:50:38 +0900274 }
275 }
276
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900277 void logDnsAsync(int netId, int[] latencies) {
Hugo Benichi5e055182016-06-01 08:50:38 +0900278 new Thread() {
279 public void run() {
280 log(netId, latencies);
281 }
282 }.start();
283 }
284
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900285 void verifyLoggedDnsEvents(DnsEvent... expected) {
286 verifyLoggedDnsEvents(0, expected);
Hugo Benichi5e055182016-06-01 08:50:38 +0900287 }
288
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900289 void verifyLoggedDnsEvents(int wait, DnsEvent... expectedEvents) {
290 verify(mLog, timeout(wait).times(expectedEvents.length)).log(mDnsEvCaptor.capture());
291 for (DnsEvent got : mDnsEvCaptor.getAllValues()) {
Hugo Benichi5e055182016-06-01 08:50:38 +0900292 OptionalInt index = IntStream.range(0, expectedEvents.length)
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900293 .filter(i -> dnsEventsEqual(expectedEvents[i], got))
Hugo Benichi5e055182016-06-01 08:50:38 +0900294 .findFirst();
295 // Don't match same expected event more than once.
296 index.ifPresent(i -> expectedEvents[i] = null);
297 assertTrue(index.isPresent());
298 }
299 }
300
301 /** equality function for DnsEvent to avoid overriding equals() and hashCode(). */
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900302 static boolean dnsEventsEqual(DnsEvent expected, DnsEvent got) {
Hugo Benichi5e055182016-06-01 08:50:38 +0900303 return (expected == got) || ((expected != null) && (got != null)
304 && (expected.netId == got.netId)
305 && Arrays.equals(expected.eventTypes, got.eventTypes)
306 && Arrays.equals(expected.returnCodes, got.returnCodes)
307 && Arrays.equals(expected.latenciesMs, got.latenciesMs));
308 }
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900309
310 static void verifyConnectEvent(String expected, IpConnectivityEvent got) {
311 try {
312 Arrays.sort(got.getConnectStatistics().latenciesMs);
313 Arrays.sort(got.getConnectStatistics().errnosCounters,
314 Comparator.comparingInt((p) -> p.key));
315 assertEquals(expected, got.toString());
316 } catch (Exception e) {
317 fail(e.toString());
318 }
319 }
Hugo Benichi5e055182016-06-01 08:50:38 +0900320}