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