blob: 05c976732505d3a5dd5e55be542464299c14769f [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 mCm = mock(ConnectivityManager.class);
66 mNetdEventListenerService = new NetdEventListenerService(mCm);
Hugo Benichi5e055182016-06-01 08:50:38 +090067 }
68
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090069 @Test
70 public void testDnsLogging() throws Exception {
71 NetworkCapabilities ncWifi = new NetworkCapabilities();
72 NetworkCapabilities ncCell = new NetworkCapabilities();
73 ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
74 ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
Hugo Benichi5e055182016-06-01 08:50:38 +090075
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090076 when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi);
77 when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell);
Hugo Benichi5e055182016-06-01 08:50:38 +090078
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090079 dnsEvent(100, EVENT_GETADDRINFO, 0, 3456);
80 dnsEvent(100, EVENT_GETADDRINFO, 0, 267);
81 dnsEvent(100, EVENT_GETHOSTBYNAME, 22, 1230);
82 dnsEvent(100, EVENT_GETADDRINFO, 3, 45);
83 dnsEvent(100, EVENT_GETADDRINFO, 1, 2111);
84 dnsEvent(100, EVENT_GETADDRINFO, 0, 450);
85 dnsEvent(100, EVENT_GETHOSTBYNAME, 200, 638);
86 dnsEvent(100, EVENT_GETHOSTBYNAME, 178, 1300);
87 dnsEvent(101, EVENT_GETADDRINFO, 0, 56);
88 dnsEvent(101, EVENT_GETADDRINFO, 0, 78);
89 dnsEvent(101, EVENT_GETADDRINFO, 0, 14);
90 dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 56);
91 dnsEvent(101, EVENT_GETADDRINFO, 0, 78);
92 dnsEvent(101, EVENT_GETADDRINFO, 0, 14);
Hugo Benichi5e055182016-06-01 08:50:38 +090093
Hugo Benichi2a5cfb92017-03-22 22:21:44 +090094 String got = flushStatistics();
95 String want = String.join("\n",
96 "dropped_events: 0",
97 "events <",
98 " if_name: \"\"",
99 " link_layer: 0",
100 " network_id: 0",
101 " time_ms: 0",
102 " transports: 0",
103 " connect_statistics <",
104 " connect_blocking_count: 0",
105 " connect_count: 0",
106 " ipv6_addr_count: 0",
107 " >",
108 ">",
109 "events <",
110 " if_name: \"\"",
111 " link_layer: 4",
112 " network_id: 100",
113 " time_ms: 0",
114 " transports: 2",
115 " dns_lookup_batch <",
116 " event_types: 1",
117 " event_types: 1",
118 " event_types: 2",
119 " event_types: 1",
120 " event_types: 1",
121 " event_types: 1",
122 " event_types: 2",
123 " event_types: 2",
124 " latencies_ms: 3456",
125 " latencies_ms: 267",
126 " latencies_ms: 1230",
127 " latencies_ms: 45",
128 " latencies_ms: 2111",
129 " latencies_ms: 450",
130 " latencies_ms: 638",
131 " latencies_ms: 1300",
132 " return_codes: 0",
133 " return_codes: 0",
134 " return_codes: 22",
135 " return_codes: 3",
136 " return_codes: 1",
137 " return_codes: 0",
138 " return_codes: 200",
139 " return_codes: 178",
140 " >",
141 ">",
142 "events <",
143 " if_name: \"\"",
144 " link_layer: 2",
145 " network_id: 101",
146 " time_ms: 0",
147 " transports: 1",
148 " dns_lookup_batch <",
149 " event_types: 1",
150 " event_types: 1",
151 " event_types: 1",
152 " event_types: 2",
153 " event_types: 1",
154 " event_types: 1",
155 " latencies_ms: 56",
156 " latencies_ms: 78",
157 " latencies_ms: 14",
158 " latencies_ms: 56",
159 " latencies_ms: 78",
160 " latencies_ms: 14",
161 " return_codes: 0",
162 " return_codes: 0",
163 " return_codes: 0",
164 " return_codes: 0",
165 " return_codes: 0",
166 " return_codes: 0",
167 " >",
168 ">",
169 "version: 2\n");
170 assertEquals(want, got);
Hugo Benichi5e055182016-06-01 08:50:38 +0900171 }
172
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900173 @Test
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900174 public void testConnectLogging() throws Exception {
175 final int OK = 0;
176 Thread[] logActions = {
177 // ignored
178 connectEventAction(OsConstants.EALREADY, 0, EXAMPLE_IPV4),
179 connectEventAction(OsConstants.EALREADY, 0, EXAMPLE_IPV6),
180 connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4),
181 connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
182 connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
183 // valid latencies
184 connectEventAction(OK, 110, EXAMPLE_IPV4),
185 connectEventAction(OK, 23, EXAMPLE_IPV4),
186 connectEventAction(OK, 45, EXAMPLE_IPV4),
187 connectEventAction(OK, 56, EXAMPLE_IPV4),
188 connectEventAction(OK, 523, EXAMPLE_IPV6),
189 connectEventAction(OK, 214, EXAMPLE_IPV6),
190 connectEventAction(OK, 67, EXAMPLE_IPV6),
191 // errors
192 connectEventAction(OsConstants.EPERM, 0, EXAMPLE_IPV4),
193 connectEventAction(OsConstants.EPERM, 0, EXAMPLE_IPV4),
194 connectEventAction(OsConstants.EAGAIN, 0, EXAMPLE_IPV4),
195 connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV4),
196 connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV4),
197 connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV6),
198 connectEventAction(OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4),
199 connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4),
200 connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
201 connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
202 connectEventAction(OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4),
203 };
204
205 for (Thread t : logActions) {
206 t.start();
207 }
208 for (Thread t : logActions) {
209 t.join();
210 }
211
212 List<IpConnectivityEvent> events = new ArrayList<>();
213 mNetdEventListenerService.flushStatistics(events);
214
215 IpConnectivityEvent got = events.get(0);
216 String want = String.join("\n",
Hugo Benichif6840502017-03-08 11:59:36 +0900217 "if_name: \"\"",
Hugo Benichi1af06a62017-01-16 14:42:56 +0900218 "link_layer: 0",
Hugo Benichif6840502017-03-08 11:59:36 +0900219 "network_id: 0",
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900220 "time_ms: 0",
Hugo Benichif6840502017-03-08 11:59:36 +0900221 "transports: 0",
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900222 "connect_statistics <",
Hugo Benichia2decca2017-02-22 14:32:27 +0900223 " connect_blocking_count: 7",
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900224 " connect_count: 12",
225 " errnos_counters <",
226 " key: 1",
227 " value: 2",
228 " >",
229 " errnos_counters <",
230 " key: 11",
231 " value: 1",
232 " >",
233 " errnos_counters <",
234 " key: 13",
235 " value: 3",
236 " >",
237 " errnos_counters <",
238 " key: 98",
239 " value: 1",
240 " >",
241 " errnos_counters <",
242 " key: 110",
243 " value: 3",
244 " >",
245 " errnos_counters <",
246 " key: 111",
247 " value: 1",
248 " >",
249 " ipv6_addr_count: 6",
250 " latencies_ms: 23",
251 " latencies_ms: 45",
252 " latencies_ms: 56",
253 " latencies_ms: 67",
254 " latencies_ms: 110",
255 " latencies_ms: 214",
256 " latencies_ms: 523",
257 ">\n");
258 verifyConnectEvent(want, got);
259 }
260
261 Thread connectEventAction(int error, int latencyMs, String ipAddr) {
262 return new Thread(() -> {
263 try {
264 mNetdEventListenerService.onConnectEvent(100, error, latencyMs, ipAddr, 80, 1);
265 } catch (Exception e) {
266 fail(e.toString());
267 }
268 });
269 }
270
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900271 void dnsEvent(int netId, int type, int result, int latency) throws Exception {
272 mNetdEventListenerService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
273 }
274
275 Thread dumpAction(long durationMs) throws Exception {
276 final long stop = System.currentTimeMillis() + durationMs;
277 final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
278 return new Thread(() -> {
279 while (System.currentTimeMillis() < stop) {
280 mNetdEventListenerService.dump(pw);
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100281 }
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900282 });
Hugo Benichi5e055182016-06-01 08:50:38 +0900283 }
Hugo Benichi0d4a3982016-11-25 11:24:22 +0900284
285 static void verifyConnectEvent(String expected, IpConnectivityEvent got) {
286 try {
287 Arrays.sort(got.getConnectStatistics().latenciesMs);
288 Arrays.sort(got.getConnectStatistics().errnosCounters,
289 Comparator.comparingInt((p) -> p.key));
290 assertEquals(expected, got.toString());
291 } catch (Exception e) {
292 fail(e.toString());
293 }
294 }
Hugo Benichi2a5cfb92017-03-22 22:21:44 +0900295
296 // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
297 String flushStatistics() throws Exception {
298 IpConnectivityMetrics metricsService =
299 new IpConnectivityMetrics(mock(Context.class), (ctx) -> 2000);
300 metricsService.mNetdListener = mNetdEventListenerService;
301
302 StringWriter buffer = new StringWriter();
303 PrintWriter writer = new PrintWriter(buffer);
304 metricsService.impl.dump(null, writer, new String[]{"flush"});
305 byte[] bytes = Base64.decode(buffer.toString(), Base64.DEFAULT);
306 return IpConnectivityLog.parseFrom(bytes).toString();
307 }
Hugo Benichi5e055182016-06-01 08:50:38 +0900308}