Home | History | Annotate | Download | only in connectivity
      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
     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
     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
    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
    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
    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 
    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 
    571     void connectEvent(int netid, int error, int latencyMs, String ipAddr) throws Exception {
    572         mNetdListener.onConnectEvent(netid, error, latencyMs, ipAddr, 80, 1);
    573     }
    574 
    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 
    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 
    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 
    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