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 import static org.mockito.Mockito.timeout;
     22 import static org.mockito.Mockito.verify;
     23 import static org.mockito.Mockito.when;
     24 import static org.junit.Assert.assertEquals;
     25 import static org.junit.Assert.assertFalse;
     26 import static org.junit.Assert.assertTrue;
     27 import static org.junit.Assert.fail;
     28 
     29 import android.content.Context;
     30 import android.net.ConnectivityManager;
     31 import android.net.ConnectivityMetricsEvent;
     32 import android.net.IIpConnectivityMetrics;
     33 import android.net.Network;
     34 import android.net.NetworkCapabilities;
     35 import android.net.metrics.ApfProgramEvent;
     36 import android.net.metrics.ApfStats;
     37 import android.net.metrics.DefaultNetworkEvent;
     38 import android.net.metrics.DhcpClientEvent;
     39 import android.net.metrics.IpConnectivityLog;
     40 import android.net.metrics.IpManagerEvent;
     41 import android.net.metrics.IpReachabilityEvent;
     42 import android.net.metrics.RaEvent;
     43 import android.net.metrics.ValidationProbeEvent;
     44 import android.system.OsConstants;
     45 import android.os.Parcelable;
     46 import android.support.test.runner.AndroidJUnit4;
     47 import android.test.suitebuilder.annotation.SmallTest;
     48 import android.util.Base64;
     49 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
     50 import java.io.PrintWriter;
     51 import java.io.StringWriter;
     52 import java.util.Collections;
     53 import java.util.Comparator;
     54 import java.util.Iterator;
     55 import java.util.List;
     56 import org.mockito.ArgumentCaptor;
     57 import org.mockito.Mock;
     58 import org.mockito.MockitoAnnotations;
     59 import org.junit.Before;
     60 import org.junit.Test;
     61 import org.junit.runner.RunWith;
     62 
     63 @RunWith(AndroidJUnit4.class)
     64 @SmallTest
     65 public class IpConnectivityMetricsTest {
     66     static final IpReachabilityEvent FAKE_EV =
     67             new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED);
     68 
     69     private static final String EXAMPLE_IPV4 = "192.0.2.1";
     70     private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
     71 
     72     @Mock Context mCtx;
     73     @Mock IIpConnectivityMetrics mMockService;
     74     @Mock ConnectivityManager mCm;
     75 
     76     IpConnectivityMetrics mService;
     77     NetdEventListenerService mNetdListener;
     78 
     79     @Before
     80     public void setUp() {
     81         MockitoAnnotations.initMocks(this);
     82         mService = new IpConnectivityMetrics(mCtx, (ctx) -> 2000);
     83         mNetdListener = new NetdEventListenerService(mCm);
     84         mService.mNetdListener = mNetdListener;
     85     }
     86 
     87     @Test
     88     public void testLoggingEvents() throws Exception {
     89         IpConnectivityLog logger = new IpConnectivityLog(mMockService);
     90 
     91         assertTrue(logger.log(1, FAKE_EV));
     92         assertTrue(logger.log(2, FAKE_EV));
     93         assertTrue(logger.log(3, FAKE_EV));
     94 
     95         List<ConnectivityMetricsEvent> got = verifyEvents(3);
     96         assertEventsEqual(expectedEvent(1), got.get(0));
     97         assertEventsEqual(expectedEvent(2), got.get(1));
     98         assertEventsEqual(expectedEvent(3), got.get(2));
     99     }
    100 
    101     @Test
    102     public void testLoggingEventsWithMultipleCallers() throws Exception {
    103         IpConnectivityLog logger = new IpConnectivityLog(mMockService);
    104 
    105         final int nCallers = 10;
    106         final int nEvents = 10;
    107         for (int n = 0; n < nCallers; n++) {
    108             final int i = n;
    109             new Thread() {
    110                 public void run() {
    111                     for (int j = 0; j < nEvents; j++) {
    112                         assertTrue(logger.log(1 + i * 100 + j, FAKE_EV));
    113                     }
    114                 }
    115             }.start();
    116         }
    117 
    118         List<ConnectivityMetricsEvent> got = verifyEvents(nCallers * nEvents, 200);
    119         Collections.sort(got, EVENT_COMPARATOR);
    120         Iterator<ConnectivityMetricsEvent> iter = got.iterator();
    121         for (int i = 0; i < nCallers; i++) {
    122             for (int j = 0; j < nEvents; j++) {
    123                 int expectedTimestamp = 1 + i * 100 + j;
    124                 assertEventsEqual(expectedEvent(expectedTimestamp), iter.next());
    125             }
    126         }
    127     }
    128 
    129     @Test
    130     public void testBufferFlushing() {
    131         String output1 = getdump("flush");
    132         assertEquals("", output1);
    133 
    134         new IpConnectivityLog(mService.impl).log(1, FAKE_EV);
    135         String output2 = getdump("flush");
    136         assertFalse("".equals(output2));
    137 
    138         String output3 = getdump("flush");
    139         assertEquals("", output3);
    140     }
    141 
    142     @Test
    143     public void testRateLimiting() {
    144         final IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
    145         final ApfProgramEvent ev = new ApfProgramEvent();
    146         final long fakeTimestamp = 1;
    147 
    148         int attempt = 100; // More than burst quota, but less than buffer size.
    149         for (int i = 0; i < attempt; i++) {
    150             logger.log(ev);
    151         }
    152 
    153         String output1 = getdump("flush");
    154         assertFalse("".equals(output1));
    155 
    156         for (int i = 0; i < attempt; i++) {
    157             assertFalse("expected event to be dropped", logger.log(fakeTimestamp, ev));
    158         }
    159 
    160         String output2 = getdump("flush");
    161         assertEquals("", output2);
    162     }
    163 
    164     @Test
    165     public void testEndToEndLogging() throws Exception {
    166         // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
    167         IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
    168 
    169         NetworkCapabilities ncWifi = new NetworkCapabilities();
    170         NetworkCapabilities ncCell = new NetworkCapabilities();
    171         ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
    172         ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
    173 
    174         when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi);
    175         when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell);
    176 
    177         ApfStats apfStats = new ApfStats();
    178         apfStats.durationMs = 45000;
    179         apfStats.receivedRas = 10;
    180         apfStats.matchingRas = 2;
    181         apfStats.droppedRas = 2;
    182         apfStats.parseErrors = 2;
    183         apfStats.zeroLifetimeRas = 1;
    184         apfStats.programUpdates = 4;
    185         apfStats.programUpdatesAll = 7;
    186         apfStats.programUpdatesAllowingMulticast = 3;
    187         apfStats.maxProgramSize = 2048;
    188 
    189         ValidationProbeEvent validationEv = new ValidationProbeEvent();
    190         validationEv.durationMs = 40730;
    191         validationEv.probeType = ValidationProbeEvent.PROBE_HTTP;
    192         validationEv.returnCode = 204;
    193 
    194         Parcelable[] events = {
    195             new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED),
    196             new DhcpClientEvent("SomeState", 192),
    197             new DefaultNetworkEvent(102, new int[]{1,2,3}, 101, true, false),
    198             new IpManagerEvent(IpManagerEvent.PROVISIONING_OK, 5678),
    199             validationEv,
    200             apfStats,
    201             new RaEvent(2000, 400, 300, -1, 1000, -1)
    202         };
    203 
    204         for (int i = 0; i < events.length; i++) {
    205             ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent();
    206             ev.timestamp = 100 * (i + 1);
    207             ev.ifname = "wlan0";
    208             ev.data = events[i];
    209             logger.log(ev);
    210         }
    211 
    212         // netId, errno, latency, destination
    213         connectEvent(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4);
    214         connectEvent(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6);
    215         connectEvent(100, 0, 110, EXAMPLE_IPV4);
    216         connectEvent(101, 0, 23, EXAMPLE_IPV4);
    217         connectEvent(101, 0, 45, EXAMPLE_IPV6);
    218         connectEvent(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4);
    219 
    220         // netId, type, return code, latency
    221         dnsEvent(100, EVENT_GETADDRINFO, 0, 3456);
    222         dnsEvent(100, EVENT_GETADDRINFO, 3, 45);
    223         dnsEvent(100, EVENT_GETHOSTBYNAME, 0, 638);
    224         dnsEvent(101, EVENT_GETADDRINFO, 0, 56);
    225         dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 34);
    226 
    227         // iface, uid
    228         wakeupEvent("wlan0", 1000);
    229         wakeupEvent("rmnet0", 10123);
    230         wakeupEvent("wlan0", 1000);
    231         wakeupEvent("rmnet0", 10008);
    232         wakeupEvent("wlan0", -1);
    233         wakeupEvent("wlan0", 10008);
    234         wakeupEvent("rmnet0", 1000);
    235 
    236         String want = String.join("\n",
    237                 "dropped_events: 0",
    238                 "events <",
    239                 "  if_name: \"\"",
    240                 "  link_layer: 4",
    241                 "  network_id: 0",
    242                 "  time_ms: 100",
    243                 "  transports: 0",
    244                 "  ip_reachability_event <",
    245                 "    event_type: 512",
    246                 "    if_name: \"\"",
    247                 "  >",
    248                 ">",
    249                 "events <",
    250                 "  if_name: \"\"",
    251                 "  link_layer: 4",
    252                 "  network_id: 0",
    253                 "  time_ms: 200",
    254                 "  transports: 0",
    255                 "  dhcp_event <",
    256                 "    duration_ms: 192",
    257                 "    if_name: \"\"",
    258                 "    state_transition: \"SomeState\"",
    259                 "  >",
    260                 ">",
    261                 "events <",
    262                 "  if_name: \"\"",
    263                 "  link_layer: 4",
    264                 "  network_id: 0",
    265                 "  time_ms: 300",
    266                 "  transports: 0",
    267                 "  default_network_event <",
    268                 "    default_network_duration_ms: 0",
    269                 "    final_score: 0",
    270                 "    initial_score: 0",
    271                 "    ip_support: 0",
    272                 "    network_id <",
    273                 "      network_id: 102",
    274                 "    >",
    275                 "    no_default_network_duration_ms: 0",
    276                 "    previous_network_id <",
    277                 "      network_id: 101",
    278                 "    >",
    279                 "    previous_network_ip_support: 1",
    280                 "    transport_types: 1",
    281                 "    transport_types: 2",
    282                 "    transport_types: 3",
    283                 "  >",
    284                 ">",
    285                 "events <",
    286                 "  if_name: \"\"",
    287                 "  link_layer: 4",
    288                 "  network_id: 0",
    289                 "  time_ms: 400",
    290                 "  transports: 0",
    291                 "  ip_provisioning_event <",
    292                 "    event_type: 1",
    293                 "    if_name: \"\"",
    294                 "    latency_ms: 5678",
    295                 "  >",
    296                 ">",
    297                 "events <",
    298                 "  if_name: \"\"",
    299                 "  link_layer: 4",
    300                 "  network_id: 0",
    301                 "  time_ms: 500",
    302                 "  transports: 0",
    303                 "  validation_probe_event <",
    304                 "    latency_ms: 40730",
    305                 "    probe_result: 204",
    306                 "    probe_type: 1",
    307                 "  >",
    308                 ">",
    309                 "events <",
    310                 "  if_name: \"\"",
    311                 "  link_layer: 4",
    312                 "  network_id: 0",
    313                 "  time_ms: 600",
    314                 "  transports: 0",
    315                 "  apf_statistics <",
    316                 "    dropped_ras: 2",
    317                 "    duration_ms: 45000",
    318                 "    matching_ras: 2",
    319                 "    max_program_size: 2048",
    320                 "    parse_errors: 2",
    321                 "    program_updates: 4",
    322                 "    program_updates_all: 7",
    323                 "    program_updates_allowing_multicast: 3",
    324                 "    received_ras: 10",
    325                 "    total_packet_dropped: 0",
    326                 "    total_packet_processed: 0",
    327                 "    zero_lifetime_ras: 1",
    328                 "  >",
    329                 ">",
    330                 "events <",
    331                 "  if_name: \"\"",
    332                 "  link_layer: 4",
    333                 "  network_id: 0",
    334                 "  time_ms: 700",
    335                 "  transports: 0",
    336                 "  ra_event <",
    337                 "    dnssl_lifetime: -1",
    338                 "    prefix_preferred_lifetime: 300",
    339                 "    prefix_valid_lifetime: 400",
    340                 "    rdnss_lifetime: 1000",
    341                 "    route_info_lifetime: -1",
    342                 "    router_lifetime: 2000",
    343                 "  >",
    344                 ">",
    345                 "events <",
    346                 "  if_name: \"\"",
    347                 "  link_layer: 4",
    348                 "  network_id: 100",
    349                 "  time_ms: 0",
    350                 "  transports: 2",
    351                 "  connect_statistics <",
    352                 "    connect_blocking_count: 1",
    353                 "    connect_count: 3",
    354                 "    errnos_counters <",
    355                 "      key: 11",
    356                 "      value: 1",
    357                 "    >",
    358                 "    ipv6_addr_count: 1",
    359                 "    latencies_ms: 110",
    360                 "  >",
    361                 ">",
    362                 "events <",
    363                 "  if_name: \"\"",
    364                 "  link_layer: 2",
    365                 "  network_id: 101",
    366                 "  time_ms: 0",
    367                 "  transports: 1",
    368                 "  connect_statistics <",
    369                 "    connect_blocking_count: 2",
    370                 "    connect_count: 2",
    371                 "    ipv6_addr_count: 1",
    372                 "    latencies_ms: 23",
    373                 "    latencies_ms: 45",
    374                 "  >",
    375                 ">",
    376                 "events <",
    377                 "  if_name: \"\"",
    378                 "  link_layer: 4",
    379                 "  network_id: 100",
    380                 "  time_ms: 0",
    381                 "  transports: 2",
    382                 "  dns_lookup_batch <",
    383                 "    event_types: 1",
    384                 "    event_types: 1",
    385                 "    event_types: 2",
    386                 "    getaddrinfo_error_count: 0",
    387                 "    getaddrinfo_query_count: 0",
    388                 "    gethostbyname_error_count: 0",
    389                 "    gethostbyname_query_count: 0",
    390                 "    latencies_ms: 3456",
    391                 "    latencies_ms: 45",
    392                 "    latencies_ms: 638",
    393                 "    return_codes: 0",
    394                 "    return_codes: 3",
    395                 "    return_codes: 0",
    396                 "  >",
    397                 ">",
    398                 "events <",
    399                 "  if_name: \"\"",
    400                 "  link_layer: 2",
    401                 "  network_id: 101",
    402                 "  time_ms: 0",
    403                 "  transports: 1",
    404                 "  dns_lookup_batch <",
    405                 "    event_types: 1",
    406                 "    event_types: 2",
    407                 "    getaddrinfo_error_count: 0",
    408                 "    getaddrinfo_query_count: 0",
    409                 "    gethostbyname_error_count: 0",
    410                 "    gethostbyname_query_count: 0",
    411                 "    latencies_ms: 56",
    412                 "    latencies_ms: 34",
    413                 "    return_codes: 0",
    414                 "    return_codes: 0",
    415                 "  >",
    416                 ">",
    417                 "events <",
    418                 "  if_name: \"\"",
    419                 "  link_layer: 2",
    420                 "  network_id: 0",
    421                 "  time_ms: 0",
    422                 "  transports: 0",
    423                 "  wakeup_stats <",
    424                 "    application_wakeups: 2",
    425                 "    duration_sec: 0",
    426                 "    no_uid_wakeups: 0",
    427                 "    non_application_wakeups: 0",
    428                 "    root_wakeups: 0",
    429                 "    system_wakeups: 1",
    430                 "    total_wakeups: 3",
    431                 "  >",
    432                 ">",
    433                 "events <",
    434                 "  if_name: \"\"",
    435                 "  link_layer: 4",
    436                 "  network_id: 0",
    437                 "  time_ms: 0",
    438                 "  transports: 0",
    439                 "  wakeup_stats <",
    440                 "    application_wakeups: 1",
    441                 "    duration_sec: 0",
    442                 "    no_uid_wakeups: 1",
    443                 "    non_application_wakeups: 0",
    444                 "    root_wakeups: 0",
    445                 "    system_wakeups: 2",
    446                 "    total_wakeups: 4",
    447                 "  >",
    448                 ">",
    449                 "version: 2\n");
    450 
    451         verifySerialization(want, getdump("flush"));
    452     }
    453 
    454     String getdump(String ... command) {
    455         StringWriter buffer = new StringWriter();
    456         PrintWriter writer = new PrintWriter(buffer);
    457         mService.impl.dump(null, writer, command);
    458         return buffer.toString();
    459     }
    460 
    461     void connectEvent(int netid, int error, int latencyMs, String ipAddr) throws Exception {
    462         mNetdListener.onConnectEvent(netid, error, latencyMs, ipAddr, 80, 1);
    463     }
    464 
    465     void dnsEvent(int netId, int type, int result, int latency) throws Exception {
    466         mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
    467     }
    468 
    469     void wakeupEvent(String iface, int uid) throws Exception {
    470         String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
    471         mNetdListener.onWakeupEvent(prefix, uid, uid, 0);
    472     }
    473 
    474     List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception {
    475         ArgumentCaptor<ConnectivityMetricsEvent> captor =
    476                 ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);
    477         verify(mMockService, timeout(timeoutMs).times(n)).logEvent(captor.capture());
    478         return captor.getAllValues();
    479     }
    480 
    481     List<ConnectivityMetricsEvent> verifyEvents(int n) throws Exception {
    482         return verifyEvents(n, 10);
    483     }
    484 
    485     static void verifySerialization(String want, String output) {
    486         try {
    487             byte[] got = Base64.decode(output, Base64.DEFAULT);
    488             IpConnectivityLogClass.IpConnectivityLog log =
    489                     IpConnectivityLogClass.IpConnectivityLog.parseFrom(got);
    490             assertEquals(want, log.toString());
    491         } catch (Exception e) {
    492             fail(e.toString());
    493         }
    494     }
    495 
    496     static String joinLines(String ... elems) {
    497         StringBuilder b = new StringBuilder();
    498         for (String s : elems) {
    499             b.append(s).append("\n");
    500         }
    501         return b.toString();
    502     }
    503 
    504     static ConnectivityMetricsEvent expectedEvent(int timestamp) {
    505         ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent();
    506         ev.timestamp = timestamp;
    507         ev.data = FAKE_EV;
    508         return ev;
    509     }
    510 
    511     /** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */
    512     static void assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got) {
    513         assertEquals(expected.timestamp, got.timestamp);
    514         assertEquals(expected.data, got.data);
    515     }
    516 
    517     static final Comparator<ConnectivityMetricsEvent> EVENT_COMPARATOR =
    518         Comparator.comparingLong((ev) -> ev.timestamp);
    519 }
    520