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