blob: 3a93b9432ca2437027a951ac7f9cf0a30983c8f9 [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 Benichi2a5cfb92017-03-22 22:21:44 +090019import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
20import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
21import static org.junit.Assert.assertEquals;
Hugo Benichif562ac32017-09-04 13:24:43 +090022import static org.junit.Assert.assertTrue;
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090023import static org.junit.Assert.fail;
Hugo Benichi5e055182016-06-01 08:50:38 +090024import static org.mockito.Mockito.any;
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090025import static org.mockito.Mockito.mock;
Hugo Benichi5e055182016-06-01 08:50:38 +090026import static org.mockito.Mockito.anyInt;
27import static org.mockito.Mockito.eq;
28import static org.mockito.Mockito.timeout;
29import static org.mockito.Mockito.times;
30import static org.mockito.Mockito.verify;
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090031import static org.mockito.Mockito.when;
Hugo Benichi5e055182016-06-01 08:50:38 +090032
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090033import android.content.Context;
34import android.net.ConnectivityManager;
35import android.net.Network;
36import android.net.NetworkCapabilities;
37import android.support.test.runner.AndroidJUnit4;
38import android.system.OsConstants;
39import android.test.suitebuilder.annotation.SmallTest;
40import android.util.Base64;
41import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.DNSLookupBatch;
42import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
43import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
44import java.io.FileOutputStream;
45import java.io.PrintWriter;
46import java.io.StringWriter;
47import java.util.ArrayList;
48import java.util.Arrays;
49import java.util.Comparator;
50import java.util.List;
51import org.junit.Before;
52import org.junit.Test;
53import org.junit.runner.RunWith;
Hugo Benichi5e055182016-06-01 08:50:38 +090054
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090055@RunWith(AndroidJUnit4.class)
56@SmallTest
57public class NetdEventListenerServiceTest {
Hugo Benichi0d4a3982016-11-25 11:24:22 +090058 private static final String EXAMPLE_IPV4 = "192.0.2.1";
59 private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
60
Michal Karpinski4f261032016-09-26 09:20:25 +010061 NetdEventListenerService mNetdEventListenerService;
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090062 ConnectivityManager mCm;
Hugo Benichi5e055182016-06-01 08:50:38 +090063
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090064 @Before
Hugo Benichi5e055182016-06-01 08:50:38 +090065 public void setUp() {
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090066 NetworkCapabilities ncWifi = new NetworkCapabilities();
67 NetworkCapabilities ncCell = new NetworkCapabilities();
68 ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
69 ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
Hugo Benichi5e055182016-06-01 08:50:38 +090070
Hugo Benichi5eb90532017-03-23 18:38:22 +090071 mCm = mock(ConnectivityManager.class);
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090072 when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi);
73 when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell);
Hugo Benichi5e055182016-06-01 08:50:38 +090074
Hugo Benichi5eb90532017-03-23 18:38:22 +090075 mNetdEventListenerService = new NetdEventListenerService(mCm);
76 }
77
78 @Test
Hugo Benichif562ac32017-09-04 13:24:43 +090079 public void testWakeupEventLogging() throws Exception {
80 final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH;
81
82 // Assert no events
83 String[] events1 = listNetdEvent();
84 assertEquals(new String[]{""}, events1);
85
86 long now = System.currentTimeMillis();
87 String prefix = "iface:wlan0";
88 int[] uids = { 10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004 };
89 for (int uid : uids) {
90 mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
91 }
92
93 String[] events2 = listNetdEvent();
94 assertEquals(uids.length, events2.length);
95 for (int i = 0; i < uids.length; i++) {
96 String got = events2[i];
97 assertContains(got, "wlan0");
98 assertContains(got, "uid: " + uids[i]);
99 }
100
101 int uid = 20000;
102 for (int i = 0; i < BUFFER_LENGTH * 2; i++) {
103 long ts = now + 10;
104 mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, ts);
105 }
106
107 // Assert there are BUFFER_LENGTH events all with uid 20000
108 String[] events3 = listNetdEvent();
109 assertEquals(BUFFER_LENGTH, events3.length);
110 for (String got : events3) {
111 assertContains(got, "wlan0");
112 assertContains(got, "uid: " + uid);
113 }
114
115 uid = 45678;
116 mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
117
118 String[] events4 = listNetdEvent();
119 String lastEvent = events4[events4.length - 1];
120 assertContains(lastEvent, "wlan0");
121 assertContains(lastEvent, "uid: " + uid);
122 }
123
124 @Test
Hugo Benichi5eb90532017-03-23 18:38:22 +0900125 public void testDnsLogging() throws Exception {
126 asyncDump(100);
127
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900128 dnsEvent(100, EVENT_GETADDRINFO, 0, 3456);
129 dnsEvent(100, EVENT_GETADDRINFO, 0, 267);
130 dnsEvent(100, EVENT_GETHOSTBYNAME, 22, 1230);
131 dnsEvent(100, EVENT_GETADDRINFO, 3, 45);
132 dnsEvent(100, EVENT_GETADDRINFO, 1, 2111);
133 dnsEvent(100, EVENT_GETADDRINFO, 0, 450);
134 dnsEvent(100, EVENT_GETHOSTBYNAME, 200, 638);
135 dnsEvent(100, EVENT_GETHOSTBYNAME, 178, 1300);
136 dnsEvent(101, EVENT_GETADDRINFO, 0, 56);
137 dnsEvent(101, EVENT_GETADDRINFO, 0, 78);
138 dnsEvent(101, EVENT_GETADDRINFO, 0, 14);
139 dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 56);
140 dnsEvent(101, EVENT_GETADDRINFO, 0, 78);
141 dnsEvent(101, EVENT_GETADDRINFO, 0, 14);
Hugo Benichi5e055182016-06-01 08:50:38 +0900142
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900143 String got = flushStatistics();
144 String want = String.join("\n",
145 "dropped_events: 0",
146 "events <",
147 " if_name: \"\"",
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900148 " link_layer: 4",
149 " network_id: 100",
150 " time_ms: 0",
151 " transports: 2",
152 " dns_lookup_batch <",
153 " event_types: 1",
154 " event_types: 1",
155 " event_types: 2",
156 " event_types: 1",
157 " event_types: 1",
158 " event_types: 1",
159 " event_types: 2",
160 " event_types: 2",
Hugo Benichi4eccf782017-07-18 14:28:27 +0900161 " getaddrinfo_error_count: 0",
162 " getaddrinfo_query_count: 0",
163 " gethostbyname_error_count: 0",
164 " gethostbyname_query_count: 0",
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900165 " latencies_ms: 3456",
166 " latencies_ms: 267",
167 " latencies_ms: 1230",
168 " latencies_ms: 45",
169 " latencies_ms: 2111",
170 " latencies_ms: 450",
171 " latencies_ms: 638",
172 " latencies_ms: 1300",
173 " return_codes: 0",
174 " return_codes: 0",
175 " return_codes: 22",
176 " return_codes: 3",
177 " return_codes: 1",
178 " return_codes: 0",
179 " return_codes: 200",
180 " return_codes: 178",
181 " >",
182 ">",
183 "events <",
184 " if_name: \"\"",
185 " link_layer: 2",
186 " network_id: 101",
187 " time_ms: 0",
188 " transports: 1",
189 " dns_lookup_batch <",
190 " event_types: 1",
191 " event_types: 1",
192 " event_types: 1",
193 " event_types: 2",
194 " event_types: 1",
195 " event_types: 1",
Hugo Benichi4eccf782017-07-18 14:28:27 +0900196 " getaddrinfo_error_count: 0",
197 " getaddrinfo_query_count: 0",
198 " gethostbyname_error_count: 0",
199 " gethostbyname_query_count: 0",
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900200 " latencies_ms: 56",
201 " latencies_ms: 78",
202 " latencies_ms: 14",
203 " latencies_ms: 56",
204 " latencies_ms: 78",
205 " latencies_ms: 14",
206 " return_codes: 0",
207 " return_codes: 0",
208 " return_codes: 0",
209 " return_codes: 0",
210 " return_codes: 0",
211 " return_codes: 0",
212 " >",
213 ">",
214 "version: 2\n");
215 assertEquals(want, got);
Hugo Benichi5e055182016-06-01 08:50:38 +0900216 }
217
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900218 @Test
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900219 public void testConnectLogging() throws Exception {
Hugo Benichi5eb90532017-03-23 18:38:22 +0900220 asyncDump(100);
221
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900222 final int OK = 0;
223 Thread[] logActions = {
224 // ignored
Hugo Benichi5eb90532017-03-23 18:38:22 +0900225 connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4),
226 connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV6),
227 connectEventAction(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4),
228 connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
229 connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900230 // valid latencies
Hugo Benichi5eb90532017-03-23 18:38:22 +0900231 connectEventAction(100, OK, 110, EXAMPLE_IPV4),
232 connectEventAction(100, OK, 23, EXAMPLE_IPV4),
233 connectEventAction(100, OK, 45, EXAMPLE_IPV4),
234 connectEventAction(101, OK, 56, EXAMPLE_IPV4),
235 connectEventAction(101, OK, 523, EXAMPLE_IPV6),
236 connectEventAction(101, OK, 214, EXAMPLE_IPV6),
237 connectEventAction(101, OK, 67, EXAMPLE_IPV6),
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900238 // errors
Hugo Benichi5eb90532017-03-23 18:38:22 +0900239 connectEventAction(100, OsConstants.EPERM, 0, EXAMPLE_IPV4),
240 connectEventAction(101, OsConstants.EPERM, 0, EXAMPLE_IPV4),
241 connectEventAction(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4),
242 connectEventAction(100, OsConstants.EACCES, 0, EXAMPLE_IPV4),
243 connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV4),
244 connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV6),
245 connectEventAction(100, OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4),
246 connectEventAction(101, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4),
247 connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
248 connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
249 connectEventAction(101, OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4),
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900250 };
251
252 for (Thread t : logActions) {
253 t.start();
254 }
255 for (Thread t : logActions) {
256 t.join();
257 }
258
Hugo Benichi5eb90532017-03-23 18:38:22 +0900259 String got = flushStatistics();
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900260 String want = String.join("\n",
Hugo Benichi5eb90532017-03-23 18:38:22 +0900261 "dropped_events: 0",
262 "events <",
263 " if_name: \"\"",
264 " link_layer: 4",
265 " network_id: 100",
266 " time_ms: 0",
267 " transports: 2",
268 " connect_statistics <",
269 " connect_blocking_count: 3",
270 " connect_count: 6",
271 " errnos_counters <",
272 " key: 1",
273 " value: 1",
274 " >",
275 " errnos_counters <",
276 " key: 11",
277 " value: 1",
278 " >",
279 " errnos_counters <",
280 " key: 13",
281 " value: 1",
282 " >",
283 " errnos_counters <",
284 " key: 98",
285 " value: 1",
286 " >",
287 " errnos_counters <",
288 " key: 110",
289 " value: 2",
290 " >",
291 " ipv6_addr_count: 1",
292 " latencies_ms: 23",
293 " latencies_ms: 45",
294 " latencies_ms: 110",
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900295 " >",
Hugo Benichi5eb90532017-03-23 18:38:22 +0900296 ">",
297 "events <",
298 " if_name: \"\"",
299 " link_layer: 2",
300 " network_id: 101",
301 " time_ms: 0",
302 " transports: 1",
303 " connect_statistics <",
304 " connect_blocking_count: 4",
305 " connect_count: 6",
306 " errnos_counters <",
307 " key: 1",
308 " value: 1",
309 " >",
310 " errnos_counters <",
311 " key: 13",
312 " value: 2",
313 " >",
314 " errnos_counters <",
315 " key: 110",
316 " value: 1",
317 " >",
318 " errnos_counters <",
319 " key: 111",
320 " value: 1",
321 " >",
322 " ipv6_addr_count: 5",
323 " latencies_ms: 56",
324 " latencies_ms: 67",
325 " latencies_ms: 214",
326 " latencies_ms: 523",
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900327 " >",
Hugo Benichi5eb90532017-03-23 18:38:22 +0900328 ">",
329 "version: 2\n");
330 assertEquals(want, got);
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900331 }
332
Hugo Benichi5eb90532017-03-23 18:38:22 +0900333 Thread connectEventAction(int netId, int error, int latencyMs, String ipAddr) {
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900334 return new Thread(() -> {
335 try {
Hugo Benichi5eb90532017-03-23 18:38:22 +0900336 mNetdEventListenerService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1);
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900337 } catch (Exception e) {
338 fail(e.toString());
339 }
340 });
341 }
342
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900343 void dnsEvent(int netId, int type, int result, int latency) throws Exception {
344 mNetdEventListenerService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
345 }
346
Hugo Benichi5eb90532017-03-23 18:38:22 +0900347 void asyncDump(long durationMs) throws Exception {
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900348 final long stop = System.currentTimeMillis() + durationMs;
349 final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
Hugo Benichi5eb90532017-03-23 18:38:22 +0900350 new Thread(() -> {
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900351 while (System.currentTimeMillis() < stop) {
352 mNetdEventListenerService.dump(pw);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100353 }
Hugo Benichi5eb90532017-03-23 18:38:22 +0900354 }).start();
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900355 }
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900356
357 // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
358 String flushStatistics() throws Exception {
359 IpConnectivityMetrics metricsService =
360 new IpConnectivityMetrics(mock(Context.class), (ctx) -> 2000);
361 metricsService.mNetdListener = mNetdEventListenerService;
362
363 StringWriter buffer = new StringWriter();
364 PrintWriter writer = new PrintWriter(buffer);
365 metricsService.impl.dump(null, writer, new String[]{"flush"});
366 byte[] bytes = Base64.decode(buffer.toString(), Base64.DEFAULT);
Hugo Benichi5eb90532017-03-23 18:38:22 +0900367 IpConnectivityLog log = IpConnectivityLog.parseFrom(bytes);
368 for (IpConnectivityEvent ev : log.events) {
369 if (ev.getConnectStatistics() == null) {
370 continue;
371 }
372 // Sort repeated fields of connect() events arriving in non-deterministic order.
373 Arrays.sort(ev.getConnectStatistics().latenciesMs);
374 Arrays.sort(ev.getConnectStatistics().errnosCounters,
375 Comparator.comparingInt((p) -> p.key));
376 }
377 return log.toString();
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900378 }
Hugo Benichif562ac32017-09-04 13:24:43 +0900379
380 String[] listNetdEvent() throws Exception {
381 StringWriter buffer = new StringWriter();
382 PrintWriter writer = new PrintWriter(buffer);
383 mNetdEventListenerService.list(writer);
384 return buffer.toString().split("\\n");
385 }
386
387 static void assertContains(String got, String want) {
388 assertTrue(got + " did not contain \"" + want + "\"", got.contains(want));
389 }
Hugo Benichi5e055182016-06-01 08:50:38 +0900390}