• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.server.connectivity;
18 
19 import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
20 import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
21 
22 import static com.android.testutils.MiscAsserts.assertStringContains;
23 
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.fail;
26 import static org.mockito.ArgumentMatchers.any;
27 import static org.mockito.Mockito.mock;
28 import static org.mockito.Mockito.verify;
29 
30 import android.content.Context;
31 import android.net.ConnectivityManager;
32 import android.net.Network;
33 import android.net.NetworkCapabilities;
34 import android.os.Build;
35 import android.system.OsConstants;
36 import android.test.suitebuilder.annotation.SmallTest;
37 import android.util.Base64;
38 
39 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
40 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
41 import com.android.testutils.DevSdkIgnoreRule;
42 import com.android.testutils.DevSdkIgnoreRunner;
43 
44 import org.junit.Before;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 import org.mockito.ArgumentCaptor;
48 
49 import java.io.FileOutputStream;
50 import java.io.PrintWriter;
51 import java.io.StringWriter;
52 import java.util.Arrays;
53 import java.util.Comparator;
54 import java.util.List;
55 
56 @RunWith(DevSdkIgnoreRunner.class)
57 @SmallTest
58 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
59 public class NetdEventListenerServiceTest {
60     private static final String EXAMPLE_IPV4 = "192.0.2.1";
61     private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
62 
63     private static final byte[] MAC_ADDR =
64             {(byte)0x84, (byte)0xc9, (byte)0xb2, (byte)0x6a, (byte)0xed, (byte)0x4b};
65 
66     NetdEventListenerService mService;
67     ConnectivityManager mCm;
68     private static final NetworkCapabilities CAPABILITIES_WIFI = new NetworkCapabilities.Builder()
69             .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
70             .build();
71     private static final NetworkCapabilities CAPABILITIES_CELL = new NetworkCapabilities.Builder()
72             .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
73             .build();
74 
75     @Before
setUp()76     public void setUp() {
77         mCm = mock(ConnectivityManager.class);
78         mService = new NetdEventListenerService(mCm);
79     }
80 
81     @Test
testWakeupEventLogging()82     public void testWakeupEventLogging() throws Exception {
83         final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH;
84         final long now = System.currentTimeMillis();
85         final String iface = "wlan0";
86         final byte[] mac = MAC_ADDR;
87         final String srcIp = "192.168.2.1";
88         final String dstIp = "192.168.2.23";
89         final String srcIp6 = "2001:db8:4:fd00:a585:13d1:6a23:4fb4";
90         final String dstIp6 = "2001:db8:4006:807::200a";
91         final int sport = 2356;
92         final int dport = 13489;
93 
94         final int v4 = 0x800;
95         final int v6 = 0x86dd;
96         final int tcp = 6;
97         final int udp = 17;
98         final int icmp6 = 58;
99 
100         // Baseline without any event
101         String[] baseline = listNetdEvent();
102 
103         int[] uids = {10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004};
104         wakeupEvent(iface, uids[0], v4, tcp, mac, srcIp, dstIp, sport, dport, now);
105         wakeupEvent(iface, uids[1], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
106         wakeupEvent(iface, uids[2], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
107         wakeupEvent(iface, uids[3], v4, icmp6, mac, srcIp, dstIp, sport, dport, now);
108         wakeupEvent(iface, uids[4], v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
109         wakeupEvent(iface, uids[5], v4, tcp, mac, srcIp, dstIp, sport, dport, now);
110         wakeupEvent(iface, uids[6], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
111         wakeupEvent(iface, uids[7], v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
112         wakeupEvent(iface, uids[8], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
113 
114         String[] events2 = remove(listNetdEvent(), baseline);
115         int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line
116         assertEquals(expectedLength2, events2.length);
117         assertStringContains(events2[0], "WakeupStats");
118         assertStringContains(events2[0], "wlan0");
119         assertStringContains(events2[0], "0x800");
120         assertStringContains(events2[0], "0x86dd");
121         for (int i = 0; i < uids.length; i++) {
122             String got = events2[i+1];
123             assertStringContains(got, "WakeupEvent");
124             assertStringContains(got, "wlan0");
125             assertStringContains(got, "uid: " + uids[i]);
126         }
127 
128         int uid = 20000;
129         for (int i = 0; i < BUFFER_LENGTH * 2; i++) {
130             long ts = now + 10;
131             wakeupEvent(iface, uid, 0x800, 6, mac, srcIp, dstIp, 23, 24, ts);
132         }
133 
134         String[] events3 = remove(listNetdEvent(), baseline);
135         int expectedLength3 = BUFFER_LENGTH + 1; // +1 for the WakeupStats line
136         assertEquals(expectedLength3, events3.length);
137         assertStringContains(events2[0], "WakeupStats");
138         assertStringContains(events2[0], "wlan0");
139         for (int i = 1; i < expectedLength3; i++) {
140             String got = events3[i];
141             assertStringContains(got, "WakeupEvent");
142             assertStringContains(got, "wlan0");
143             assertStringContains(got, "uid: " + uid);
144         }
145 
146         uid = 45678;
147         wakeupEvent(iface, uid, 0x800, 6, mac, srcIp, dstIp, 23, 24, now);
148 
149         String[] events4 = remove(listNetdEvent(), baseline);
150         String lastEvent = events4[events4.length - 1];
151         assertStringContains(lastEvent, "WakeupEvent");
152         assertStringContains(lastEvent, "wlan0");
153         assertStringContains(lastEvent, "uid: " + uid);
154     }
155 
156     @Test
testWakeupStatsLogging()157     public void testWakeupStatsLogging() throws Exception {
158         final byte[] mac = MAC_ADDR;
159         final String srcIp = "192.168.2.1";
160         final String dstIp = "192.168.2.23";
161         final String srcIp6 = "2401:fa00:4:fd00:a585:13d1:6a23:4fb4";
162         final String dstIp6 = "2404:6800:4006:807::200a";
163         final int sport = 2356;
164         final int dport = 13489;
165         final long now = 1001L;
166 
167         final int v4 = 0x800;
168         final int v6 = 0x86dd;
169         final int tcp = 6;
170         final int udp = 17;
171         final int icmp6 = 58;
172 
173         wakeupEvent("wlan0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
174         wakeupEvent("rmnet0", 10123, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
175         wakeupEvent("wlan0", 1000, v4, udp, mac, srcIp, dstIp, sport, dport, now);
176         wakeupEvent("rmnet0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
177         wakeupEvent("wlan0", -1, v6, icmp6, mac, srcIp6, dstIp6, sport, dport, now);
178         wakeupEvent("wlan0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
179         wakeupEvent("rmnet0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
180         wakeupEvent("wlan0", 10004, v4, udp, mac, srcIp, dstIp, sport, dport, now);
181         wakeupEvent("wlan0", 1000, v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
182         wakeupEvent("wlan0", 0, v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
183         wakeupEvent("wlan0", -1, v6, icmp6, mac, srcIp6, dstIp6, sport, dport, now);
184         wakeupEvent("rmnet0", 10052, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
185         wakeupEvent("wlan0", 0, v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
186         wakeupEvent("rmnet0", 1000, v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
187         wakeupEvent("wlan0", 1010, v4, udp, mac, srcIp, dstIp, sport, dport, now);
188 
189         String got = flushStatistics();
190         String want = String.join("\n",
191                 "dropped_events: 0",
192                 "events <",
193                 "  if_name: \"\"",
194                 "  link_layer: 2",
195                 "  network_id: 0",
196                 "  time_ms: 0",
197                 "  transports: 0",
198                 "  wakeup_stats <",
199                 "    application_wakeups: 3",
200                 "    duration_sec: 0",
201                 "    ethertype_counts <",
202                 "      key: 2048",
203                 "      value: 4",
204                 "    >",
205                 "    ethertype_counts <",
206                 "      key: 34525",
207                 "      value: 1",
208                 "    >",
209                 "    ip_next_header_counts <",
210                 "      key: 6",
211                 "      value: 5",
212                 "    >",
213                 "    l2_broadcast_count: 0",
214                 "    l2_multicast_count: 0",
215                 "    l2_unicast_count: 5",
216                 "    no_uid_wakeups: 0",
217                 "    non_application_wakeups: 0",
218                 "    root_wakeups: 0",
219                 "    system_wakeups: 2",
220                 "    total_wakeups: 5",
221                 "  >",
222                 ">",
223                 "events <",
224                 "  if_name: \"\"",
225                 "  link_layer: 4",
226                 "  network_id: 0",
227                 "  time_ms: 0",
228                 "  transports: 0",
229                 "  wakeup_stats <",
230                 "    application_wakeups: 2",
231                 "    duration_sec: 0",
232                 "    ethertype_counts <",
233                 "      key: 2048",
234                 "      value: 5",
235                 "    >",
236                 "    ethertype_counts <",
237                 "      key: 34525",
238                 "      value: 5",
239                 "    >",
240                 "    ip_next_header_counts <",
241                 "      key: 6",
242                 "      value: 3",
243                 "    >",
244                 "    ip_next_header_counts <",
245                 "      key: 17",
246                 "      value: 5",
247                 "    >",
248                 "    ip_next_header_counts <",
249                 "      key: 58",
250                 "      value: 2",
251                 "    >",
252                 "    l2_broadcast_count: 0",
253                 "    l2_multicast_count: 0",
254                 "    l2_unicast_count: 10",
255                 "    no_uid_wakeups: 2",
256                 "    non_application_wakeups: 1",
257                 "    root_wakeups: 2",
258                 "    system_wakeups: 3",
259                 "    total_wakeups: 10",
260                 "  >",
261                 ">",
262                 "version: 2\n");
263         assertEquals(want, got);
264     }
265 
266     @Test
testDnsLogging()267     public void testDnsLogging() throws Exception {
268         asyncDump(100);
269 
270         dnsEvent(100, EVENT_GETADDRINFO, 0, 3456);
271         dnsEvent(100, EVENT_GETADDRINFO, 0, 267);
272         dnsEvent(100, EVENT_GETHOSTBYNAME, 22, 1230);
273         dnsEvent(100, EVENT_GETADDRINFO, 3, 45);
274         dnsEvent(100, EVENT_GETADDRINFO, 1, 2111);
275         dnsEvent(100, EVENT_GETADDRINFO, 0, 450);
276         dnsEvent(100, EVENT_GETHOSTBYNAME, 200, 638);
277         dnsEvent(100, EVENT_GETHOSTBYNAME, 178, 1300);
278         dnsEvent(101, EVENT_GETADDRINFO, 0, 56);
279         dnsEvent(101, EVENT_GETADDRINFO, 0, 78);
280         dnsEvent(101, EVENT_GETADDRINFO, 0, 14);
281         dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 56);
282         dnsEvent(101, EVENT_GETADDRINFO, 0, 78);
283         dnsEvent(101, EVENT_GETADDRINFO, 0, 14);
284 
285         String got = flushStatistics();
286         String want = String.join("\n",
287                 "dropped_events: 0",
288                 "events <",
289                 "  if_name: \"\"",
290                 "  link_layer: 4",
291                 "  network_id: 100",
292                 "  time_ms: 0",
293                 "  transports: 2",
294                 "  dns_lookup_batch <",
295                 "    event_types: 1",
296                 "    event_types: 1",
297                 "    event_types: 2",
298                 "    event_types: 1",
299                 "    event_types: 1",
300                 "    event_types: 1",
301                 "    event_types: 2",
302                 "    event_types: 2",
303                 "    getaddrinfo_error_count: 0",
304                 "    getaddrinfo_query_count: 0",
305                 "    gethostbyname_error_count: 0",
306                 "    gethostbyname_query_count: 0",
307                 "    latencies_ms: 3456",
308                 "    latencies_ms: 267",
309                 "    latencies_ms: 1230",
310                 "    latencies_ms: 45",
311                 "    latencies_ms: 2111",
312                 "    latencies_ms: 450",
313                 "    latencies_ms: 638",
314                 "    latencies_ms: 1300",
315                 "    return_codes: 0",
316                 "    return_codes: 0",
317                 "    return_codes: 22",
318                 "    return_codes: 3",
319                 "    return_codes: 1",
320                 "    return_codes: 0",
321                 "    return_codes: 200",
322                 "    return_codes: 178",
323                 "  >",
324                 ">",
325                 "events <",
326                 "  if_name: \"\"",
327                 "  link_layer: 2",
328                 "  network_id: 101",
329                 "  time_ms: 0",
330                 "  transports: 1",
331                 "  dns_lookup_batch <",
332                 "    event_types: 1",
333                 "    event_types: 1",
334                 "    event_types: 1",
335                 "    event_types: 2",
336                 "    event_types: 1",
337                 "    event_types: 1",
338                 "    getaddrinfo_error_count: 0",
339                 "    getaddrinfo_query_count: 0",
340                 "    gethostbyname_error_count: 0",
341                 "    gethostbyname_query_count: 0",
342                 "    latencies_ms: 56",
343                 "    latencies_ms: 78",
344                 "    latencies_ms: 14",
345                 "    latencies_ms: 56",
346                 "    latencies_ms: 78",
347                 "    latencies_ms: 14",
348                 "    return_codes: 0",
349                 "    return_codes: 0",
350                 "    return_codes: 0",
351                 "    return_codes: 0",
352                 "    return_codes: 0",
353                 "    return_codes: 0",
354                 "  >",
355                 ">",
356                 "version: 2\n");
357         assertEquals(want, got);
358     }
359 
360     @Test
testConnectLogging()361     public void testConnectLogging() throws Exception {
362         asyncDump(100);
363 
364         final int OK = 0;
365         Thread[] logActions = {
366             // ignored
367             connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4),
368             connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV6),
369             connectEventAction(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4),
370             connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
371             connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
372             // valid latencies
373             connectEventAction(100, OK, 110, EXAMPLE_IPV4),
374             connectEventAction(100, OK, 23, EXAMPLE_IPV4),
375             connectEventAction(100, OK, 45, EXAMPLE_IPV4),
376             connectEventAction(101, OK, 56, EXAMPLE_IPV4),
377             connectEventAction(101, OK, 523, EXAMPLE_IPV6),
378             connectEventAction(101, OK, 214, EXAMPLE_IPV6),
379             connectEventAction(101, OK, 67, EXAMPLE_IPV6),
380             // errors
381             connectEventAction(100, OsConstants.EPERM, 0, EXAMPLE_IPV4),
382             connectEventAction(101, OsConstants.EPERM, 0, EXAMPLE_IPV4),
383             connectEventAction(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4),
384             connectEventAction(100, OsConstants.EACCES, 0, EXAMPLE_IPV4),
385             connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV4),
386             connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV6),
387             connectEventAction(100, OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4),
388             connectEventAction(101, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4),
389             connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
390             connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
391             connectEventAction(101, OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4),
392         };
393 
394         for (Thread t : logActions) {
395             t.start();
396         }
397         for (Thread t : logActions) {
398             t.join();
399         }
400 
401         String got = flushStatistics();
402         String want = String.join("\n",
403                 "dropped_events: 0",
404                 "events <",
405                 "  if_name: \"\"",
406                 "  link_layer: 4",
407                 "  network_id: 100",
408                 "  time_ms: 0",
409                 "  transports: 2",
410                 "  connect_statistics <",
411                 "    connect_blocking_count: 3",
412                 "    connect_count: 6",
413                 "    errnos_counters <",
414                 "      key: 1",
415                 "      value: 1",
416                 "    >",
417                 "    errnos_counters <",
418                 "      key: 11",
419                 "      value: 1",
420                 "    >",
421                 "    errnos_counters <",
422                 "      key: 13",
423                 "      value: 1",
424                 "    >",
425                 "    errnos_counters <",
426                 "      key: 98",
427                 "      value: 1",
428                 "    >",
429                 "    errnos_counters <",
430                 "      key: 110",
431                 "      value: 2",
432                 "    >",
433                 "    ipv6_addr_count: 1",
434                 "    latencies_ms: 23",
435                 "    latencies_ms: 45",
436                 "    latencies_ms: 110",
437                 "  >",
438                 ">",
439                 "events <",
440                 "  if_name: \"\"",
441                 "  link_layer: 2",
442                 "  network_id: 101",
443                 "  time_ms: 0",
444                 "  transports: 1",
445                 "  connect_statistics <",
446                 "    connect_blocking_count: 4",
447                 "    connect_count: 6",
448                 "    errnos_counters <",
449                 "      key: 1",
450                 "      value: 1",
451                 "    >",
452                 "    errnos_counters <",
453                 "      key: 13",
454                 "      value: 2",
455                 "    >",
456                 "    errnos_counters <",
457                 "      key: 110",
458                 "      value: 1",
459                 "    >",
460                 "    errnos_counters <",
461                 "      key: 111",
462                 "      value: 1",
463                 "    >",
464                 "    ipv6_addr_count: 5",
465                 "    latencies_ms: 56",
466                 "    latencies_ms: 67",
467                 "    latencies_ms: 214",
468                 "    latencies_ms: 523",
469                 "  >",
470                 ">",
471                 "version: 2\n");
472         assertEquals(want, got);
473     }
474 
setCapabilities(int netId)475     private void setCapabilities(int netId) {
476         final ArgumentCaptor<ConnectivityManager.NetworkCallback> networkCallback =
477                 ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
478         verify(mCm).registerNetworkCallback(any(), networkCallback.capture());
479         networkCallback.getValue().onCapabilitiesChanged(new Network(netId),
480                 netId == 100 ? CAPABILITIES_WIFI : CAPABILITIES_CELL);
481     }
482 
connectEventAction(int netId, int error, int latencyMs, String ipAddr)483     Thread connectEventAction(int netId, int error, int latencyMs, String ipAddr) {
484         setCapabilities(netId);
485         return new Thread(() -> {
486             try {
487                 mService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1);
488             } catch (Exception e) {
489                 fail(e.toString());
490             }
491         });
492     }
493 
494     void dnsEvent(int netId, int type, int result, int latency) throws Exception {
495         setCapabilities(netId);
496         mService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
497     }
498 
499     void wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp,
500             String dstIp, int sport, int dport, long now) throws Exception {
501         String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
502         mService.onWakeupEvent(prefix, uid, ether, ip, mac, srcIp, dstIp, sport, dport, now);
503     }
504 
505     void asyncDump(long durationMs) throws Exception {
506         final long stop = System.currentTimeMillis() + durationMs;
507         final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
508         new Thread(() -> {
509             while (System.currentTimeMillis() < stop) {
510                 mService.list(pw);
511             }
512         }).start();
513     }
514 
515     // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
516     String flushStatistics() throws Exception {
517         IpConnectivityMetrics metricsService =
518                 new IpConnectivityMetrics(mock(Context.class), (ctx) -> 2000);
519         metricsService.mNetdListener = mService;
520 
521         StringWriter buffer = new StringWriter();
522         PrintWriter writer = new PrintWriter(buffer);
523         metricsService.impl.dump(null, writer, new String[]{"flush"});
524         byte[] bytes = Base64.decode(buffer.toString(), Base64.DEFAULT);
525         IpConnectivityLog log = IpConnectivityLog.parseFrom(bytes);
526         for (IpConnectivityEvent ev : log.events) {
527             if (ev.getConnectStatistics() == null) {
528                 continue;
529             }
530             // Sort repeated fields of connect() events arriving in non-deterministic order.
531             Arrays.sort(ev.getConnectStatistics().latenciesMs);
532             Arrays.sort(ev.getConnectStatistics().errnosCounters,
533                     Comparator.comparingInt((p) -> p.key));
534         }
535         return log.toString();
536     }
537 
538     String[] listNetdEvent() throws Exception {
539         StringWriter buffer = new StringWriter();
540         PrintWriter writer = new PrintWriter(buffer);
541         mService.list(writer);
542         return buffer.toString().split("\\n");
543     }
544 
545     static <T> T[] remove(T[] array, T[] filtered) {
546         List<T> c = Arrays.asList(filtered);
547         int next = 0;
548         for (int i = 0; i < array.length; i++) {
549             if (c.contains(array[i])) {
550                 continue;
551             }
552             array[next++] = array[i];
553         }
554         return Arrays.copyOf(array, next);
555     }
556 }
557