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