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