blob: 2b105e5c92eec63bb6220cf2c5f781bc387611a0 [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;
Hugo Benichi60c9f632017-09-05 13:34:48 +090041
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090042import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.DNSLookupBatch;
43import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
44import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
Hugo Benichi60c9f632017-09-05 13:34:48 +090045
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090046import java.io.FileOutputStream;
47import java.io.PrintWriter;
48import java.io.StringWriter;
49import java.util.ArrayList;
50import java.util.Arrays;
51import java.util.Comparator;
52import java.util.List;
Hugo Benichi60c9f632017-09-05 13:34:48 +090053
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090054import org.junit.Before;
55import org.junit.Test;
56import org.junit.runner.RunWith;
Hugo Benichi5e055182016-06-01 08:50:38 +090057
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090058@RunWith(AndroidJUnit4.class)
59@SmallTest
60public class NetdEventListenerServiceTest {
Hugo Benichi0d4a3982016-11-25 11:24:22 +090061 private static final String EXAMPLE_IPV4 = "192.0.2.1";
62 private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
63
Michal Karpinski4f261032016-09-26 09:20:25 +010064 NetdEventListenerService mNetdEventListenerService;
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090065 ConnectivityManager mCm;
Hugo Benichi5e055182016-06-01 08:50:38 +090066
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090067 @Before
Hugo Benichi5e055182016-06-01 08:50:38 +090068 public void setUp() {
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090069 NetworkCapabilities ncWifi = new NetworkCapabilities();
70 NetworkCapabilities ncCell = new NetworkCapabilities();
71 ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
72 ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
Hugo Benichi5e055182016-06-01 08:50:38 +090073
Hugo Benichi5eb90532017-03-23 18:38:22 +090074 mCm = mock(ConnectivityManager.class);
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090075 when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi);
76 when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell);
Hugo Benichi5e055182016-06-01 08:50:38 +090077
Hugo Benichi5eb90532017-03-23 18:38:22 +090078 mNetdEventListenerService = new NetdEventListenerService(mCm);
79 }
80
81 @Test
Hugo Benichif562ac32017-09-04 13:24:43 +090082 public void testWakeupEventLogging() throws Exception {
83 final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH;
84
85 // Assert no events
86 String[] events1 = listNetdEvent();
87 assertEquals(new String[]{""}, events1);
88
89 long now = System.currentTimeMillis();
90 String prefix = "iface:wlan0";
91 int[] uids = { 10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004 };
92 for (int uid : uids) {
93 mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
94 }
95
96 String[] events2 = listNetdEvent();
Hugo Benichi60c9f632017-09-05 13:34:48 +090097 int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line
98 assertEquals(expectedLength2, events2.length);
99 assertContains(events2[0], "WakeupStats");
100 assertContains(events2[0], "wlan0");
Hugo Benichif562ac32017-09-04 13:24:43 +0900101 for (int i = 0; i < uids.length; i++) {
Hugo Benichi60c9f632017-09-05 13:34:48 +0900102 String got = events2[i+1];
103 assertContains(got, "WakeupEvent");
Hugo Benichif562ac32017-09-04 13:24:43 +0900104 assertContains(got, "wlan0");
105 assertContains(got, "uid: " + uids[i]);
106 }
107
108 int uid = 20000;
109 for (int i = 0; i < BUFFER_LENGTH * 2; i++) {
110 long ts = now + 10;
111 mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, ts);
112 }
113
Hugo Benichif562ac32017-09-04 13:24:43 +0900114 String[] events3 = listNetdEvent();
Hugo Benichi60c9f632017-09-05 13:34:48 +0900115 int expectedLength3 = BUFFER_LENGTH + 1; // +1 for the WakeupStats line
116 assertEquals(expectedLength3, events3.length);
117 assertContains(events2[0], "WakeupStats");
118 assertContains(events2[0], "wlan0");
119 for (int i = 1; i < expectedLength3; i++) {
120 String got = events3[i];
121 assertContains(got, "WakeupEvent");
Hugo Benichif562ac32017-09-04 13:24:43 +0900122 assertContains(got, "wlan0");
123 assertContains(got, "uid: " + uid);
124 }
125
126 uid = 45678;
127 mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
128
129 String[] events4 = listNetdEvent();
130 String lastEvent = events4[events4.length - 1];
Hugo Benichi60c9f632017-09-05 13:34:48 +0900131 assertContains(lastEvent, "WakeupEvent");
Hugo Benichif562ac32017-09-04 13:24:43 +0900132 assertContains(lastEvent, "wlan0");
133 assertContains(lastEvent, "uid: " + uid);
134 }
135
136 @Test
Hugo Benichi60c9f632017-09-05 13:34:48 +0900137 public void testWakeupStatsLogging() throws Exception {
138 wakeupEvent("wlan0", 1000);
139 wakeupEvent("rmnet0", 10123);
140 wakeupEvent("wlan0", 1000);
141 wakeupEvent("rmnet0", 10008);
142 wakeupEvent("wlan0", -1);
143 wakeupEvent("wlan0", 10008);
144 wakeupEvent("rmnet0", 1000);
145 wakeupEvent("wlan0", 10004);
146 wakeupEvent("wlan0", 1000);
147 wakeupEvent("wlan0", 0);
148 wakeupEvent("wlan0", -1);
149 wakeupEvent("rmnet0", 10052);
150 wakeupEvent("wlan0", 0);
151 wakeupEvent("rmnet0", 1000);
152 wakeupEvent("wlan0", 1010);
153
154 String got = flushStatistics();
155 String want = String.join("\n",
156 "dropped_events: 0",
157 "events <",
158 " if_name: \"\"",
159 " link_layer: 2",
160 " network_id: 0",
161 " time_ms: 0",
162 " transports: 0",
163 " wakeup_stats <",
164 " application_wakeups: 3",
165 " duration_sec: 0",
166 " non_application_wakeups: 0",
167 " root_wakeups: 0",
168 " system_wakeups: 2",
169 " total_wakeups: 5",
170 " unrouted_wakeups: 0",
171 " >",
172 ">",
173 "events <",
174 " if_name: \"\"",
175 " link_layer: 4",
176 " network_id: 0",
177 " time_ms: 0",
178 " transports: 0",
179 " wakeup_stats <",
180 " application_wakeups: 2",
181 " duration_sec: 0",
182 " non_application_wakeups: 1",
183 " root_wakeups: 2",
184 " system_wakeups: 3",
185 " total_wakeups: 10",
186 " unrouted_wakeups: 2",
187 " >",
188 ">",
189 "version: 2\n");
190 assertEquals(want, got);
191 }
192
193 @Test
Hugo Benichi5eb90532017-03-23 18:38:22 +0900194 public void testDnsLogging() throws Exception {
195 asyncDump(100);
196
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900197 dnsEvent(100, EVENT_GETADDRINFO, 0, 3456);
198 dnsEvent(100, EVENT_GETADDRINFO, 0, 267);
199 dnsEvent(100, EVENT_GETHOSTBYNAME, 22, 1230);
200 dnsEvent(100, EVENT_GETADDRINFO, 3, 45);
201 dnsEvent(100, EVENT_GETADDRINFO, 1, 2111);
202 dnsEvent(100, EVENT_GETADDRINFO, 0, 450);
203 dnsEvent(100, EVENT_GETHOSTBYNAME, 200, 638);
204 dnsEvent(100, EVENT_GETHOSTBYNAME, 178, 1300);
205 dnsEvent(101, EVENT_GETADDRINFO, 0, 56);
206 dnsEvent(101, EVENT_GETADDRINFO, 0, 78);
207 dnsEvent(101, EVENT_GETADDRINFO, 0, 14);
208 dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 56);
209 dnsEvent(101, EVENT_GETADDRINFO, 0, 78);
210 dnsEvent(101, EVENT_GETADDRINFO, 0, 14);
Hugo Benichi5e055182016-06-01 08:50:38 +0900211
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900212 String got = flushStatistics();
213 String want = String.join("\n",
214 "dropped_events: 0",
215 "events <",
216 " if_name: \"\"",
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900217 " link_layer: 4",
218 " network_id: 100",
219 " time_ms: 0",
220 " transports: 2",
221 " dns_lookup_batch <",
222 " event_types: 1",
223 " event_types: 1",
224 " event_types: 2",
225 " event_types: 1",
226 " event_types: 1",
227 " event_types: 1",
228 " event_types: 2",
229 " event_types: 2",
Hugo Benichi4eccf782017-07-18 14:28:27 +0900230 " getaddrinfo_error_count: 0",
231 " getaddrinfo_query_count: 0",
232 " gethostbyname_error_count: 0",
233 " gethostbyname_query_count: 0",
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900234 " latencies_ms: 3456",
235 " latencies_ms: 267",
236 " latencies_ms: 1230",
237 " latencies_ms: 45",
238 " latencies_ms: 2111",
239 " latencies_ms: 450",
240 " latencies_ms: 638",
241 " latencies_ms: 1300",
242 " return_codes: 0",
243 " return_codes: 0",
244 " return_codes: 22",
245 " return_codes: 3",
246 " return_codes: 1",
247 " return_codes: 0",
248 " return_codes: 200",
249 " return_codes: 178",
250 " >",
251 ">",
252 "events <",
253 " if_name: \"\"",
254 " link_layer: 2",
255 " network_id: 101",
256 " time_ms: 0",
257 " transports: 1",
258 " dns_lookup_batch <",
259 " event_types: 1",
260 " event_types: 1",
261 " event_types: 1",
262 " event_types: 2",
263 " event_types: 1",
264 " event_types: 1",
Hugo Benichi4eccf782017-07-18 14:28:27 +0900265 " getaddrinfo_error_count: 0",
266 " getaddrinfo_query_count: 0",
267 " gethostbyname_error_count: 0",
268 " gethostbyname_query_count: 0",
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900269 " latencies_ms: 56",
270 " latencies_ms: 78",
271 " latencies_ms: 14",
272 " latencies_ms: 56",
273 " latencies_ms: 78",
274 " latencies_ms: 14",
275 " return_codes: 0",
276 " return_codes: 0",
277 " return_codes: 0",
278 " return_codes: 0",
279 " return_codes: 0",
280 " return_codes: 0",
281 " >",
282 ">",
283 "version: 2\n");
284 assertEquals(want, got);
Hugo Benichi5e055182016-06-01 08:50:38 +0900285 }
286
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900287 @Test
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900288 public void testConnectLogging() throws Exception {
Hugo Benichi5eb90532017-03-23 18:38:22 +0900289 asyncDump(100);
290
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900291 final int OK = 0;
292 Thread[] logActions = {
293 // ignored
Hugo Benichi5eb90532017-03-23 18:38:22 +0900294 connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4),
295 connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV6),
296 connectEventAction(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4),
297 connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
298 connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900299 // valid latencies
Hugo Benichi5eb90532017-03-23 18:38:22 +0900300 connectEventAction(100, OK, 110, EXAMPLE_IPV4),
301 connectEventAction(100, OK, 23, EXAMPLE_IPV4),
302 connectEventAction(100, OK, 45, EXAMPLE_IPV4),
303 connectEventAction(101, OK, 56, EXAMPLE_IPV4),
304 connectEventAction(101, OK, 523, EXAMPLE_IPV6),
305 connectEventAction(101, OK, 214, EXAMPLE_IPV6),
306 connectEventAction(101, OK, 67, EXAMPLE_IPV6),
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900307 // errors
Hugo Benichi5eb90532017-03-23 18:38:22 +0900308 connectEventAction(100, OsConstants.EPERM, 0, EXAMPLE_IPV4),
309 connectEventAction(101, OsConstants.EPERM, 0, EXAMPLE_IPV4),
310 connectEventAction(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4),
311 connectEventAction(100, OsConstants.EACCES, 0, EXAMPLE_IPV4),
312 connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV4),
313 connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV6),
314 connectEventAction(100, OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4),
315 connectEventAction(101, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4),
316 connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
317 connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
318 connectEventAction(101, OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4),
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900319 };
320
321 for (Thread t : logActions) {
322 t.start();
323 }
324 for (Thread t : logActions) {
325 t.join();
326 }
327
Hugo Benichi5eb90532017-03-23 18:38:22 +0900328 String got = flushStatistics();
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900329 String want = String.join("\n",
Hugo Benichi5eb90532017-03-23 18:38:22 +0900330 "dropped_events: 0",
331 "events <",
332 " if_name: \"\"",
333 " link_layer: 4",
334 " network_id: 100",
335 " time_ms: 0",
336 " transports: 2",
337 " connect_statistics <",
338 " connect_blocking_count: 3",
339 " connect_count: 6",
340 " errnos_counters <",
341 " key: 1",
342 " value: 1",
343 " >",
344 " errnos_counters <",
345 " key: 11",
346 " value: 1",
347 " >",
348 " errnos_counters <",
349 " key: 13",
350 " value: 1",
351 " >",
352 " errnos_counters <",
353 " key: 98",
354 " value: 1",
355 " >",
356 " errnos_counters <",
357 " key: 110",
358 " value: 2",
359 " >",
360 " ipv6_addr_count: 1",
361 " latencies_ms: 23",
362 " latencies_ms: 45",
363 " latencies_ms: 110",
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900364 " >",
Hugo Benichi5eb90532017-03-23 18:38:22 +0900365 ">",
366 "events <",
367 " if_name: \"\"",
368 " link_layer: 2",
369 " network_id: 101",
370 " time_ms: 0",
371 " transports: 1",
372 " connect_statistics <",
373 " connect_blocking_count: 4",
374 " connect_count: 6",
375 " errnos_counters <",
376 " key: 1",
377 " value: 1",
378 " >",
379 " errnos_counters <",
380 " key: 13",
381 " value: 2",
382 " >",
383 " errnos_counters <",
384 " key: 110",
385 " value: 1",
386 " >",
387 " errnos_counters <",
388 " key: 111",
389 " value: 1",
390 " >",
391 " ipv6_addr_count: 5",
392 " latencies_ms: 56",
393 " latencies_ms: 67",
394 " latencies_ms: 214",
395 " latencies_ms: 523",
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900396 " >",
Hugo Benichi5eb90532017-03-23 18:38:22 +0900397 ">",
398 "version: 2\n");
399 assertEquals(want, got);
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900400 }
401
Hugo Benichi5eb90532017-03-23 18:38:22 +0900402 Thread connectEventAction(int netId, int error, int latencyMs, String ipAddr) {
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900403 return new Thread(() -> {
404 try {
Hugo Benichi5eb90532017-03-23 18:38:22 +0900405 mNetdEventListenerService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1);
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900406 } catch (Exception e) {
407 fail(e.toString());
408 }
409 });
410 }
411
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900412 void dnsEvent(int netId, int type, int result, int latency) throws Exception {
413 mNetdEventListenerService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
414 }
415
Hugo Benichi60c9f632017-09-05 13:34:48 +0900416 void wakeupEvent(String iface, int uid) throws Exception {
417 String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
418 mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, 0);
419 }
420
Hugo Benichi5eb90532017-03-23 18:38:22 +0900421 void asyncDump(long durationMs) throws Exception {
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900422 final long stop = System.currentTimeMillis() + durationMs;
423 final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
Hugo Benichi5eb90532017-03-23 18:38:22 +0900424 new Thread(() -> {
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900425 while (System.currentTimeMillis() < stop) {
426 mNetdEventListenerService.dump(pw);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100427 }
Hugo Benichi5eb90532017-03-23 18:38:22 +0900428 }).start();
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900429 }
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900430
431 // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
432 String flushStatistics() throws Exception {
433 IpConnectivityMetrics metricsService =
434 new IpConnectivityMetrics(mock(Context.class), (ctx) -> 2000);
435 metricsService.mNetdListener = mNetdEventListenerService;
436
437 StringWriter buffer = new StringWriter();
438 PrintWriter writer = new PrintWriter(buffer);
439 metricsService.impl.dump(null, writer, new String[]{"flush"});
440 byte[] bytes = Base64.decode(buffer.toString(), Base64.DEFAULT);
Hugo Benichi5eb90532017-03-23 18:38:22 +0900441 IpConnectivityLog log = IpConnectivityLog.parseFrom(bytes);
442 for (IpConnectivityEvent ev : log.events) {
443 if (ev.getConnectStatistics() == null) {
444 continue;
445 }
446 // Sort repeated fields of connect() events arriving in non-deterministic order.
447 Arrays.sort(ev.getConnectStatistics().latenciesMs);
448 Arrays.sort(ev.getConnectStatistics().errnosCounters,
449 Comparator.comparingInt((p) -> p.key));
450 }
451 return log.toString();
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900452 }
Hugo Benichif562ac32017-09-04 13:24:43 +0900453
454 String[] listNetdEvent() throws Exception {
455 StringWriter buffer = new StringWriter();
456 PrintWriter writer = new PrintWriter(buffer);
457 mNetdEventListenerService.list(writer);
458 return buffer.toString().split("\\n");
459 }
460
461 static void assertContains(String got, String want) {
462 assertTrue(got + " did not contain \"" + want + "\"", got.contains(want));
463 }
Hugo Benichi5e055182016-06-01 08:50:38 +0900464}