blob: 46f395eae214c4184943e9d3ce98d45412c8e5da [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;
22import static org.junit.Assert.fail;
Hugo Benichi5e055182016-06-01 08:50:38 +090023import static org.mockito.Mockito.any;
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090024import static org.mockito.Mockito.mock;
Hugo Benichi5e055182016-06-01 08:50:38 +090025import static org.mockito.Mockito.anyInt;
26import static org.mockito.Mockito.eq;
27import static org.mockito.Mockito.timeout;
28import static org.mockito.Mockito.times;
29import static org.mockito.Mockito.verify;
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090030import static org.mockito.Mockito.when;
Hugo Benichi5e055182016-06-01 08:50:38 +090031
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090032import android.content.Context;
33import android.net.ConnectivityManager;
34import android.net.Network;
35import android.net.NetworkCapabilities;
36import android.support.test.runner.AndroidJUnit4;
37import android.system.OsConstants;
38import android.test.suitebuilder.annotation.SmallTest;
39import android.util.Base64;
40import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.DNSLookupBatch;
41import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
42import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
43import java.io.FileOutputStream;
44import java.io.PrintWriter;
45import java.io.StringWriter;
46import java.util.ArrayList;
47import java.util.Arrays;
48import java.util.Comparator;
49import java.util.List;
50import org.junit.Before;
51import org.junit.Test;
52import org.junit.runner.RunWith;
Hugo Benichi5e055182016-06-01 08:50:38 +090053
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090054@RunWith(AndroidJUnit4.class)
55@SmallTest
56public class NetdEventListenerServiceTest {
Hugo Benichi0d4a3982016-11-25 11:24:22 +090057 private static final String EXAMPLE_IPV4 = "192.0.2.1";
58 private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
59
Michal Karpinski4f261032016-09-26 09:20:25 +010060 NetdEventListenerService mNetdEventListenerService;
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090061 ConnectivityManager mCm;
Hugo Benichi5e055182016-06-01 08:50:38 +090062
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090063 @Before
Hugo Benichi5e055182016-06-01 08:50:38 +090064 public void setUp() {
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090065 NetworkCapabilities ncWifi = new NetworkCapabilities();
66 NetworkCapabilities ncCell = new NetworkCapabilities();
67 ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
68 ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
Hugo Benichi5e055182016-06-01 08:50:38 +090069
Hugo Benichi5eb90532017-03-23 18:38:22 +090070 mCm = mock(ConnectivityManager.class);
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090071 when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi);
72 when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell);
Hugo Benichi5e055182016-06-01 08:50:38 +090073
Hugo Benichi5eb90532017-03-23 18:38:22 +090074 mNetdEventListenerService = new NetdEventListenerService(mCm);
75 }
76
77 @Test
78 public void testDnsLogging() throws Exception {
79 asyncDump(100);
80
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090081 dnsEvent(100, EVENT_GETADDRINFO, 0, 3456);
82 dnsEvent(100, EVENT_GETADDRINFO, 0, 267);
83 dnsEvent(100, EVENT_GETHOSTBYNAME, 22, 1230);
84 dnsEvent(100, EVENT_GETADDRINFO, 3, 45);
85 dnsEvent(100, EVENT_GETADDRINFO, 1, 2111);
86 dnsEvent(100, EVENT_GETADDRINFO, 0, 450);
87 dnsEvent(100, EVENT_GETHOSTBYNAME, 200, 638);
88 dnsEvent(100, EVENT_GETHOSTBYNAME, 178, 1300);
89 dnsEvent(101, EVENT_GETADDRINFO, 0, 56);
90 dnsEvent(101, EVENT_GETADDRINFO, 0, 78);
91 dnsEvent(101, EVENT_GETADDRINFO, 0, 14);
92 dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 56);
93 dnsEvent(101, EVENT_GETADDRINFO, 0, 78);
94 dnsEvent(101, EVENT_GETADDRINFO, 0, 14);
Hugo Benichi5e055182016-06-01 08:50:38 +090095
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090096 String got = flushStatistics();
97 String want = String.join("\n",
98 "dropped_events: 0",
99 "events <",
100 " if_name: \"\"",
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900101 " link_layer: 4",
102 " network_id: 100",
103 " time_ms: 0",
104 " transports: 2",
105 " dns_lookup_batch <",
106 " event_types: 1",
107 " event_types: 1",
108 " event_types: 2",
109 " event_types: 1",
110 " event_types: 1",
111 " event_types: 1",
112 " event_types: 2",
113 " event_types: 2",
Hugo Benichi4eccf782017-07-18 14:28:27 +0900114 " getaddrinfo_error_count: 0",
115 " getaddrinfo_query_count: 0",
116 " gethostbyname_error_count: 0",
117 " gethostbyname_query_count: 0",
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900118 " latencies_ms: 3456",
119 " latencies_ms: 267",
120 " latencies_ms: 1230",
121 " latencies_ms: 45",
122 " latencies_ms: 2111",
123 " latencies_ms: 450",
124 " latencies_ms: 638",
125 " latencies_ms: 1300",
126 " return_codes: 0",
127 " return_codes: 0",
128 " return_codes: 22",
129 " return_codes: 3",
130 " return_codes: 1",
131 " return_codes: 0",
132 " return_codes: 200",
133 " return_codes: 178",
134 " >",
135 ">",
136 "events <",
137 " if_name: \"\"",
138 " link_layer: 2",
139 " network_id: 101",
140 " time_ms: 0",
141 " transports: 1",
142 " dns_lookup_batch <",
143 " event_types: 1",
144 " event_types: 1",
145 " event_types: 1",
146 " event_types: 2",
147 " event_types: 1",
148 " event_types: 1",
Hugo Benichi4eccf782017-07-18 14:28:27 +0900149 " getaddrinfo_error_count: 0",
150 " getaddrinfo_query_count: 0",
151 " gethostbyname_error_count: 0",
152 " gethostbyname_query_count: 0",
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900153 " latencies_ms: 56",
154 " latencies_ms: 78",
155 " latencies_ms: 14",
156 " latencies_ms: 56",
157 " latencies_ms: 78",
158 " latencies_ms: 14",
159 " return_codes: 0",
160 " return_codes: 0",
161 " return_codes: 0",
162 " return_codes: 0",
163 " return_codes: 0",
164 " return_codes: 0",
165 " >",
166 ">",
167 "version: 2\n");
168 assertEquals(want, got);
Hugo Benichi5e055182016-06-01 08:50:38 +0900169 }
170
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900171 @Test
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900172 public void testConnectLogging() throws Exception {
Hugo Benichi5eb90532017-03-23 18:38:22 +0900173 asyncDump(100);
174
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900175 final int OK = 0;
176 Thread[] logActions = {
177 // ignored
Hugo Benichi5eb90532017-03-23 18:38:22 +0900178 connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4),
179 connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV6),
180 connectEventAction(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4),
181 connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
182 connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900183 // valid latencies
Hugo Benichi5eb90532017-03-23 18:38:22 +0900184 connectEventAction(100, OK, 110, EXAMPLE_IPV4),
185 connectEventAction(100, OK, 23, EXAMPLE_IPV4),
186 connectEventAction(100, OK, 45, EXAMPLE_IPV4),
187 connectEventAction(101, OK, 56, EXAMPLE_IPV4),
188 connectEventAction(101, OK, 523, EXAMPLE_IPV6),
189 connectEventAction(101, OK, 214, EXAMPLE_IPV6),
190 connectEventAction(101, OK, 67, EXAMPLE_IPV6),
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900191 // errors
Hugo Benichi5eb90532017-03-23 18:38:22 +0900192 connectEventAction(100, OsConstants.EPERM, 0, EXAMPLE_IPV4),
193 connectEventAction(101, OsConstants.EPERM, 0, EXAMPLE_IPV4),
194 connectEventAction(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4),
195 connectEventAction(100, OsConstants.EACCES, 0, EXAMPLE_IPV4),
196 connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV4),
197 connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV6),
198 connectEventAction(100, OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4),
199 connectEventAction(101, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4),
200 connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
201 connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
202 connectEventAction(101, OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4),
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900203 };
204
205 for (Thread t : logActions) {
206 t.start();
207 }
208 for (Thread t : logActions) {
209 t.join();
210 }
211
Hugo Benichi5eb90532017-03-23 18:38:22 +0900212 String got = flushStatistics();
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900213 String want = String.join("\n",
Hugo Benichi5eb90532017-03-23 18:38:22 +0900214 "dropped_events: 0",
215 "events <",
216 " if_name: \"\"",
217 " link_layer: 4",
218 " network_id: 100",
219 " time_ms: 0",
220 " transports: 2",
221 " connect_statistics <",
222 " connect_blocking_count: 3",
223 " connect_count: 6",
224 " errnos_counters <",
225 " key: 1",
226 " value: 1",
227 " >",
228 " errnos_counters <",
229 " key: 11",
230 " value: 1",
231 " >",
232 " errnos_counters <",
233 " key: 13",
234 " value: 1",
235 " >",
236 " errnos_counters <",
237 " key: 98",
238 " value: 1",
239 " >",
240 " errnos_counters <",
241 " key: 110",
242 " value: 2",
243 " >",
244 " ipv6_addr_count: 1",
245 " latencies_ms: 23",
246 " latencies_ms: 45",
247 " latencies_ms: 110",
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900248 " >",
Hugo Benichi5eb90532017-03-23 18:38:22 +0900249 ">",
250 "events <",
251 " if_name: \"\"",
252 " link_layer: 2",
253 " network_id: 101",
254 " time_ms: 0",
255 " transports: 1",
256 " connect_statistics <",
257 " connect_blocking_count: 4",
258 " connect_count: 6",
259 " errnos_counters <",
260 " key: 1",
261 " value: 1",
262 " >",
263 " errnos_counters <",
264 " key: 13",
265 " value: 2",
266 " >",
267 " errnos_counters <",
268 " key: 110",
269 " value: 1",
270 " >",
271 " errnos_counters <",
272 " key: 111",
273 " value: 1",
274 " >",
275 " ipv6_addr_count: 5",
276 " latencies_ms: 56",
277 " latencies_ms: 67",
278 " latencies_ms: 214",
279 " latencies_ms: 523",
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900280 " >",
Hugo Benichi5eb90532017-03-23 18:38:22 +0900281 ">",
282 "version: 2\n");
283 assertEquals(want, got);
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900284 }
285
Hugo Benichi5eb90532017-03-23 18:38:22 +0900286 Thread connectEventAction(int netId, int error, int latencyMs, String ipAddr) {
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900287 return new Thread(() -> {
288 try {
Hugo Benichi5eb90532017-03-23 18:38:22 +0900289 mNetdEventListenerService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1);
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900290 } catch (Exception e) {
291 fail(e.toString());
292 }
293 });
294 }
295
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900296 void dnsEvent(int netId, int type, int result, int latency) throws Exception {
297 mNetdEventListenerService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
298 }
299
Hugo Benichi5eb90532017-03-23 18:38:22 +0900300 void asyncDump(long durationMs) throws Exception {
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900301 final long stop = System.currentTimeMillis() + durationMs;
302 final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
Hugo Benichi5eb90532017-03-23 18:38:22 +0900303 new Thread(() -> {
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900304 while (System.currentTimeMillis() < stop) {
305 mNetdEventListenerService.dump(pw);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100306 }
Hugo Benichi5eb90532017-03-23 18:38:22 +0900307 }).start();
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900308 }
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900309
310 // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
311 String flushStatistics() throws Exception {
312 IpConnectivityMetrics metricsService =
313 new IpConnectivityMetrics(mock(Context.class), (ctx) -> 2000);
314 metricsService.mNetdListener = mNetdEventListenerService;
315
316 StringWriter buffer = new StringWriter();
317 PrintWriter writer = new PrintWriter(buffer);
318 metricsService.impl.dump(null, writer, new String[]{"flush"});
319 byte[] bytes = Base64.decode(buffer.toString(), Base64.DEFAULT);
Hugo Benichi5eb90532017-03-23 18:38:22 +0900320 IpConnectivityLog log = IpConnectivityLog.parseFrom(bytes);
321 for (IpConnectivityEvent ev : log.events) {
322 if (ev.getConnectStatistics() == null) {
323 continue;
324 }
325 // Sort repeated fields of connect() events arriving in non-deterministic order.
326 Arrays.sort(ev.getConnectStatistics().latenciesMs);
327 Arrays.sort(ev.getConnectStatistics().errnosCounters,
328 Comparator.comparingInt((p) -> p.key));
329 }
330 return log.toString();
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900331 }
Hugo Benichi5e055182016-06-01 08:50:38 +0900332}