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 org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertFalse; 24 import static org.junit.Assert.fail; 25 import static org.mockito.Mockito.mock; 26 import static org.mockito.Mockito.when; 27 28 import android.content.Context; 29 import android.net.ConnectivityManager; 30 import android.net.ConnectivityMetricsEvent; 31 import android.net.IIpConnectivityMetrics; 32 import android.net.IpPrefix; 33 import android.net.LinkAddress; 34 import android.net.LinkProperties; 35 import android.net.Network; 36 import android.net.NetworkCapabilities; 37 import android.net.RouteInfo; 38 import android.net.metrics.ApfProgramEvent; 39 import android.net.metrics.ApfStats; 40 import android.net.metrics.DhcpClientEvent; 41 import android.net.metrics.IpConnectivityLog; 42 import android.net.metrics.IpManagerEvent; 43 import android.net.metrics.IpReachabilityEvent; 44 import android.net.metrics.RaEvent; 45 import android.net.metrics.ValidationProbeEvent; 46 import android.os.Parcelable; 47 import android.system.OsConstants; 48 import android.test.suitebuilder.annotation.SmallTest; 49 import android.util.Base64; 50 51 import androidx.test.runner.AndroidJUnit4; 52 53 import com.android.internal.util.BitUtils; 54 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass; 55 56 import org.junit.Before; 57 import org.junit.Test; 58 import org.junit.runner.RunWith; 59 import org.mockito.Mock; 60 import org.mockito.MockitoAnnotations; 61 62 import java.io.PrintWriter; 63 import java.io.StringWriter; 64 65 @RunWith(AndroidJUnit4.class) 66 @SmallTest 67 public class IpConnectivityMetricsTest { 68 static final IpReachabilityEvent FAKE_EV = 69 new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED); 70 71 private static final String EXAMPLE_IPV4 = "192.0.2.1"; 72 private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1"; 73 74 private static final byte[] MAC_ADDR = 75 {(byte)0x84, (byte)0xc9, (byte)0xb2, (byte)0x6a, (byte)0xed, (byte)0x4b}; 76 77 @Mock Context mCtx; 78 @Mock IIpConnectivityMetrics mMockService; 79 @Mock ConnectivityManager mCm; 80 81 IpConnectivityMetrics mService; 82 NetdEventListenerService mNetdListener; 83 84 @Before setUp()85 public void setUp() { 86 MockitoAnnotations.initMocks(this); 87 mService = new IpConnectivityMetrics(mCtx, (ctx) -> 2000); 88 mNetdListener = new NetdEventListenerService(mCm); 89 mService.mNetdListener = mNetdListener; 90 } 91 92 @Test testBufferFlushing()93 public void testBufferFlushing() { 94 String output1 = getdump("flush"); 95 assertEquals("", output1); 96 97 new IpConnectivityLog(mService.impl).log(1, FAKE_EV); 98 String output2 = getdump("flush"); 99 assertFalse("".equals(output2)); 100 101 String output3 = getdump("flush"); 102 assertEquals("", output3); 103 } 104 105 @Test testRateLimiting()106 public void testRateLimiting() { 107 final IpConnectivityLog logger = new IpConnectivityLog(mService.impl); 108 final ApfProgramEvent ev = new ApfProgramEvent.Builder().build(); 109 final long fakeTimestamp = 1; 110 111 int attempt = 100; // More than burst quota, but less than buffer size. 112 for (int i = 0; i < attempt; i++) { 113 logger.log(ev); 114 } 115 116 String output1 = getdump("flush"); 117 assertFalse("".equals(output1)); 118 119 for (int i = 0; i < attempt; i++) { 120 assertFalse("expected event to be dropped", logger.log(fakeTimestamp, ev)); 121 } 122 123 String output2 = getdump("flush"); 124 assertEquals("", output2); 125 } 126 127 @Test testDefaultNetworkEvents()128 public void testDefaultNetworkEvents() throws Exception { 129 final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR}); 130 final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI}); 131 132 NetworkAgentInfo[][] defaultNetworks = { 133 // nothing -> cell 134 {null, makeNai(100, 10, false, true, cell)}, 135 // cell -> wifi 136 {makeNai(100, 50, true, true, cell), makeNai(101, 20, true, false, wifi)}, 137 // wifi -> nothing 138 {makeNai(101, 60, true, false, wifi), null}, 139 // nothing -> cell 140 {null, makeNai(102, 10, true, true, cell)}, 141 // cell -> wifi 142 {makeNai(102, 50, true, true, cell), makeNai(103, 20, true, false, wifi)}, 143 }; 144 145 long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs; 146 long durationMs = 1001; 147 for (NetworkAgentInfo[] pair : defaultNetworks) { 148 timeMs += durationMs; 149 durationMs += durationMs; 150 mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, pair[1], pair[0]); 151 } 152 153 String want = String.join("\n", 154 "dropped_events: 0", 155 "events <", 156 " if_name: \"\"", 157 " link_layer: 5", 158 " network_id: 0", 159 " time_ms: 0", 160 " transports: 0", 161 " default_network_event <", 162 " default_network_duration_ms: 1001", 163 " final_score: 0", 164 " initial_score: 0", 165 " ip_support: 0", 166 " no_default_network_duration_ms: 0", 167 " previous_default_network_link_layer: 0", 168 " previous_network_ip_support: 0", 169 " validation_duration_ms: 0", 170 " >", 171 ">", 172 "events <", 173 " if_name: \"\"", 174 " link_layer: 2", 175 " network_id: 100", 176 " time_ms: 0", 177 " transports: 1", 178 " default_network_event <", 179 " default_network_duration_ms: 2002", 180 " final_score: 50", 181 " initial_score: 10", 182 " ip_support: 3", 183 " no_default_network_duration_ms: 0", 184 " previous_default_network_link_layer: 0", 185 " previous_network_ip_support: 0", 186 " validation_duration_ms: 2002", 187 " >", 188 ">", 189 "events <", 190 " if_name: \"\"", 191 " link_layer: 4", 192 " network_id: 101", 193 " time_ms: 0", 194 " transports: 2", 195 " default_network_event <", 196 " default_network_duration_ms: 4004", 197 " final_score: 60", 198 " initial_score: 20", 199 " ip_support: 1", 200 " no_default_network_duration_ms: 0", 201 " previous_default_network_link_layer: 2", 202 " previous_network_ip_support: 0", 203 " validation_duration_ms: 4004", 204 " >", 205 ">", 206 "events <", 207 " if_name: \"\"", 208 " link_layer: 5", 209 " network_id: 0", 210 " time_ms: 0", 211 " transports: 0", 212 " default_network_event <", 213 " default_network_duration_ms: 8008", 214 " final_score: 0", 215 " initial_score: 0", 216 " ip_support: 0", 217 " no_default_network_duration_ms: 0", 218 " previous_default_network_link_layer: 4", 219 " previous_network_ip_support: 0", 220 " validation_duration_ms: 0", 221 " >", 222 ">", 223 "events <", 224 " if_name: \"\"", 225 " link_layer: 2", 226 " network_id: 102", 227 " time_ms: 0", 228 " transports: 1", 229 " default_network_event <", 230 " default_network_duration_ms: 16016", 231 " final_score: 50", 232 " initial_score: 10", 233 " ip_support: 3", 234 " no_default_network_duration_ms: 0", 235 " previous_default_network_link_layer: 4", 236 " previous_network_ip_support: 0", 237 " validation_duration_ms: 16016", 238 " >", 239 ">", 240 "version: 2\n"); 241 242 verifySerialization(want, getdump("flush")); 243 } 244 245 @Test testEndToEndLogging()246 public void testEndToEndLogging() throws Exception { 247 // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. 248 IpConnectivityLog logger = new IpConnectivityLog(mService.impl); 249 250 NetworkCapabilities ncWifi = new NetworkCapabilities(); 251 NetworkCapabilities ncCell = new NetworkCapabilities(); 252 ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); 253 ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); 254 255 when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi); 256 when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell); 257 258 ApfStats apfStats = new ApfStats.Builder() 259 .setDurationMs(45000) 260 .setReceivedRas(10) 261 .setMatchingRas(2) 262 .setDroppedRas(2) 263 .setParseErrors(2) 264 .setZeroLifetimeRas(1) 265 .setProgramUpdates(4) 266 .setProgramUpdatesAll(7) 267 .setProgramUpdatesAllowingMulticast(3) 268 .setMaxProgramSize(2048) 269 .build(); 270 271 final ValidationProbeEvent validationEv = new ValidationProbeEvent.Builder() 272 .setDurationMs(40730) 273 .setProbeType(ValidationProbeEvent.PROBE_HTTP, true) 274 .setReturnCode(204) 275 .build(); 276 277 final DhcpClientEvent event = new DhcpClientEvent.Builder() 278 .setMsg("SomeState") 279 .setDurationMs(192) 280 .build(); 281 Parcelable[] events = { 282 new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED), event, 283 new IpManagerEvent(IpManagerEvent.PROVISIONING_OK, 5678), 284 validationEv, 285 apfStats, 286 new RaEvent(2000, 400, 300, -1, 1000, -1) 287 }; 288 289 for (int i = 0; i < events.length; i++) { 290 ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent(); 291 ev.timestamp = 100 * (i + 1); 292 ev.ifname = "wlan0"; 293 ev.data = events[i]; 294 logger.log(ev); 295 } 296 297 // netId, errno, latency, destination 298 connectEvent(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4); 299 connectEvent(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6); 300 connectEvent(100, 0, 110, EXAMPLE_IPV4); 301 connectEvent(101, 0, 23, EXAMPLE_IPV4); 302 connectEvent(101, 0, 45, EXAMPLE_IPV6); 303 connectEvent(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4); 304 305 // netId, type, return code, latency 306 dnsEvent(100, EVENT_GETADDRINFO, 0, 3456); 307 dnsEvent(100, EVENT_GETADDRINFO, 3, 45); 308 dnsEvent(100, EVENT_GETHOSTBYNAME, 0, 638); 309 dnsEvent(101, EVENT_GETADDRINFO, 0, 56); 310 dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 34); 311 312 // iface, uid 313 final byte[] mac = {0x48, 0x7c, 0x2b, 0x6a, 0x3e, 0x4b}; 314 final String srcIp = "192.168.2.1"; 315 final String dstIp = "192.168.2.23"; 316 final int sport = 2356; 317 final int dport = 13489; 318 final long now = 1001L; 319 final int v4 = 0x800; 320 final int tcp = 6; 321 final int udp = 17; 322 wakeupEvent("wlan0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L); 323 wakeupEvent("wlan0", 10123, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L); 324 wakeupEvent("wlan0", 1000, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L); 325 wakeupEvent("wlan0", 10008, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L); 326 wakeupEvent("wlan0", -1, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L); 327 wakeupEvent("wlan0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L); 328 329 long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs; 330 final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR}); 331 final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI}); 332 NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell); 333 NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi); 334 mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 200, cellNai, null); 335 mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 300, wifiNai, cellNai); 336 337 String want = String.join("\n", 338 "dropped_events: 0", 339 "events <", 340 " if_name: \"\"", 341 " link_layer: 4", 342 " network_id: 0", 343 " time_ms: 100", 344 " transports: 0", 345 " ip_reachability_event <", 346 " event_type: 512", 347 " if_name: \"\"", 348 " >", 349 ">", 350 "events <", 351 " if_name: \"\"", 352 " link_layer: 4", 353 " network_id: 0", 354 " time_ms: 200", 355 " transports: 0", 356 " dhcp_event <", 357 " duration_ms: 192", 358 " if_name: \"\"", 359 " state_transition: \"SomeState\"", 360 " >", 361 ">", 362 "events <", 363 " if_name: \"\"", 364 " link_layer: 4", 365 " network_id: 0", 366 " time_ms: 300", 367 " transports: 0", 368 " ip_provisioning_event <", 369 " event_type: 1", 370 " if_name: \"\"", 371 " latency_ms: 5678", 372 " >", 373 ">", 374 "events <", 375 " if_name: \"\"", 376 " link_layer: 4", 377 " network_id: 0", 378 " time_ms: 400", 379 " transports: 0", 380 " validation_probe_event <", 381 " latency_ms: 40730", 382 " probe_result: 204", 383 " probe_type: 257", 384 " >", 385 ">", 386 "events <", 387 " if_name: \"\"", 388 " link_layer: 4", 389 " network_id: 0", 390 " time_ms: 500", 391 " transports: 0", 392 " apf_statistics <", 393 " dropped_ras: 2", 394 " duration_ms: 45000", 395 " matching_ras: 2", 396 " max_program_size: 2048", 397 " parse_errors: 2", 398 " program_updates: 4", 399 " program_updates_all: 7", 400 " program_updates_allowing_multicast: 3", 401 " received_ras: 10", 402 " total_packet_dropped: 0", 403 " total_packet_processed: 0", 404 " zero_lifetime_ras: 1", 405 " >", 406 ">", 407 "events <", 408 " if_name: \"\"", 409 " link_layer: 4", 410 " network_id: 0", 411 " time_ms: 600", 412 " transports: 0", 413 " ra_event <", 414 " dnssl_lifetime: -1", 415 " prefix_preferred_lifetime: 300", 416 " prefix_valid_lifetime: 400", 417 " rdnss_lifetime: 1000", 418 " route_info_lifetime: -1", 419 " router_lifetime: 2000", 420 " >", 421 ">", 422 "events <", 423 " if_name: \"\"", 424 " link_layer: 5", 425 " network_id: 0", 426 " time_ms: 0", 427 " transports: 0", 428 " default_network_event <", 429 " default_network_duration_ms: 200", 430 " final_score: 0", 431 " initial_score: 0", 432 " ip_support: 0", 433 " no_default_network_duration_ms: 0", 434 " previous_default_network_link_layer: 0", 435 " previous_network_ip_support: 0", 436 " validation_duration_ms: 0", 437 " >", 438 ">", 439 "events <", 440 " if_name: \"\"", 441 " link_layer: 2", 442 " network_id: 100", 443 " time_ms: 0", 444 " transports: 1", 445 " default_network_event <", 446 " default_network_duration_ms: 100", 447 " final_score: 50", 448 " initial_score: 50", 449 " ip_support: 2", 450 " no_default_network_duration_ms: 0", 451 " previous_default_network_link_layer: 0", 452 " previous_network_ip_support: 0", 453 " validation_duration_ms: 100", 454 " >", 455 ">", 456 "events <", 457 " if_name: \"\"", 458 " link_layer: 4", 459 " network_id: 100", 460 " time_ms: 0", 461 " transports: 2", 462 " connect_statistics <", 463 " connect_blocking_count: 1", 464 " connect_count: 3", 465 " errnos_counters <", 466 " key: 11", 467 " value: 1", 468 " >", 469 " ipv6_addr_count: 1", 470 " latencies_ms: 110", 471 " >", 472 ">", 473 "events <", 474 " if_name: \"\"", 475 " link_layer: 2", 476 " network_id: 101", 477 " time_ms: 0", 478 " transports: 1", 479 " connect_statistics <", 480 " connect_blocking_count: 2", 481 " connect_count: 2", 482 " ipv6_addr_count: 1", 483 " latencies_ms: 23", 484 " latencies_ms: 45", 485 " >", 486 ">", 487 "events <", 488 " if_name: \"\"", 489 " link_layer: 4", 490 " network_id: 100", 491 " time_ms: 0", 492 " transports: 2", 493 " dns_lookup_batch <", 494 " event_types: 1", 495 " event_types: 1", 496 " event_types: 2", 497 " getaddrinfo_error_count: 0", 498 " getaddrinfo_query_count: 0", 499 " gethostbyname_error_count: 0", 500 " gethostbyname_query_count: 0", 501 " latencies_ms: 3456", 502 " latencies_ms: 45", 503 " latencies_ms: 638", 504 " return_codes: 0", 505 " return_codes: 3", 506 " return_codes: 0", 507 " >", 508 ">", 509 "events <", 510 " if_name: \"\"", 511 " link_layer: 2", 512 " network_id: 101", 513 " time_ms: 0", 514 " transports: 1", 515 " dns_lookup_batch <", 516 " event_types: 1", 517 " event_types: 2", 518 " getaddrinfo_error_count: 0", 519 " getaddrinfo_query_count: 0", 520 " gethostbyname_error_count: 0", 521 " gethostbyname_query_count: 0", 522 " latencies_ms: 56", 523 " latencies_ms: 34", 524 " return_codes: 0", 525 " return_codes: 0", 526 " >", 527 ">", 528 "events <", 529 " if_name: \"\"", 530 " link_layer: 4", 531 " network_id: 0", 532 " time_ms: 0", 533 " transports: 0", 534 " wakeup_stats <", 535 " application_wakeups: 3", 536 " duration_sec: 0", 537 " ethertype_counts <", 538 " key: 2048", 539 " value: 6", 540 " >", 541 " ip_next_header_counts <", 542 " key: 6", 543 " value: 3", 544 " >", 545 " ip_next_header_counts <", 546 " key: 17", 547 " value: 3", 548 " >", 549 " l2_broadcast_count: 0", 550 " l2_multicast_count: 0", 551 " l2_unicast_count: 6", 552 " no_uid_wakeups: 1", 553 " non_application_wakeups: 0", 554 " root_wakeups: 0", 555 " system_wakeups: 2", 556 " total_wakeups: 6", 557 " >", 558 ">", 559 "version: 2\n"); 560 561 verifySerialization(want, getdump("flush")); 562 } 563 getdump(String .... command)564 String getdump(String ... command) { 565 StringWriter buffer = new StringWriter(); 566 PrintWriter writer = new PrintWriter(buffer); 567 mService.impl.dump(null, writer, command); 568 return buffer.toString(); 569 } 570 connectEvent(int netid, int error, int latencyMs, String ipAddr)571 void connectEvent(int netid, int error, int latencyMs, String ipAddr) throws Exception { 572 mNetdListener.onConnectEvent(netid, error, latencyMs, ipAddr, 80, 1); 573 } 574 dnsEvent(int netId, int type, int result, int latency)575 void dnsEvent(int netId, int type, int result, int latency) throws Exception { 576 mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0); 577 } 578 wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp, String dstIp, int sport, int dport, long now)579 void wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp, 580 String dstIp, int sport, int dport, long now) throws Exception { 581 String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface; 582 mNetdListener.onWakeupEvent(prefix, uid, ether, ip, mac, srcIp, dstIp, sport, dport, now); 583 } 584 makeNai(int netId, int score, boolean ipv4, boolean ipv6, long transports)585 NetworkAgentInfo makeNai(int netId, int score, boolean ipv4, boolean ipv6, long transports) { 586 NetworkAgentInfo nai = mock(NetworkAgentInfo.class); 587 when(nai.network()).thenReturn(new Network(netId)); 588 when(nai.getCurrentScore()).thenReturn(score); 589 nai.linkProperties = new LinkProperties(); 590 nai.networkCapabilities = new NetworkCapabilities(); 591 nai.lastValidated = true; 592 for (int t : BitUtils.unpackBits(transports)) { 593 nai.networkCapabilities.addTransportType(t); 594 } 595 if (ipv4) { 596 nai.linkProperties.addLinkAddress(new LinkAddress("192.0.2.12/24")); 597 nai.linkProperties.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"))); 598 } 599 if (ipv6) { 600 nai.linkProperties.addLinkAddress(new LinkAddress("2001:db8:dead:beef:f00::a0/64")); 601 nai.linkProperties.addRoute(new RouteInfo(new IpPrefix("::/0"))); 602 } 603 return nai; 604 } 605 606 607 verifySerialization(String want, String output)608 static void verifySerialization(String want, String output) { 609 try { 610 byte[] got = Base64.decode(output, Base64.DEFAULT); 611 IpConnectivityLogClass.IpConnectivityLog log = 612 IpConnectivityLogClass.IpConnectivityLog.parseFrom(got); 613 assertEquals(want, log.toString()); 614 } catch (Exception e) { 615 fail(e.toString()); 616 } 617 } 618 } 619