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.junit.Assert.assertEquals;
     22 import static org.junit.Assert.assertTrue;
     23 import static org.junit.Assert.fail;
     24 import static org.mockito.Mockito.any;
     25 import static org.mockito.Mockito.mock;
     26 import static org.mockito.Mockito.anyInt;
     27 import static org.mockito.Mockito.eq;
     28 import static org.mockito.Mockito.timeout;
     29 import static org.mockito.Mockito.times;
     30 import static org.mockito.Mockito.verify;
     31 import static org.mockito.Mockito.when;
     32 
     33 import android.content.Context;
     34 import android.net.ConnectivityManager;
     35 import android.net.Network;
     36 import android.net.NetworkCapabilities;
     37 import android.support.test.runner.AndroidJUnit4;
     38 import android.system.OsConstants;
     39 import android.test.suitebuilder.annotation.SmallTest;
     40 import android.util.Base64;
     41 
     42 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.DNSLookupBatch;
     43 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
     44 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
     45 
     46 import java.io.FileOutputStream;
     47 import java.io.PrintWriter;
     48 import java.io.StringWriter;
     49 import java.util.ArrayList;
     50 import java.util.Arrays;
     51 import java.util.Comparator;
     52 import java.util.List;
     53 
     54 import org.junit.Before;
     55 import org.junit.Test;
     56 import org.junit.runner.RunWith;
     57 
     58 @RunWith(AndroidJUnit4.class)
     59 @SmallTest
     60 public class NetdEventListenerServiceTest {
     61     private static final String EXAMPLE_IPV4 = "192.0.2.1";
     62     private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
     63 
     64     NetdEventListenerService mNetdEventListenerService;
     65     ConnectivityManager mCm;
     66 
     67     @Before
     68     public void setUp() {
     69         NetworkCapabilities ncWifi = new NetworkCapabilities();
     70         NetworkCapabilities ncCell = new NetworkCapabilities();
     71         ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
     72         ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
     73 
     74         mCm = mock(ConnectivityManager.class);
     75         when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi);
     76         when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell);
     77 
     78         mNetdEventListenerService = new NetdEventListenerService(mCm);
     79     }
     80 
     81     @Test
     82     public void testWakeupEventLogging() throws Exception {
     83         final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH;
     84 
     85         // Assert no events
     86         String[] events1 = listNetdEvent();
     87         assertEquals(new String[]{""}, events1);
     88 
     89         long now = System.currentTimeMillis();
     90         String prefix = "iface:wlan0";
     91         int[] uids = { 10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004 };
     92         for (int uid : uids) {
     93             mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
     94         }
     95 
     96         String[] events2 = listNetdEvent();
     97         int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line
     98         assertEquals(expectedLength2, events2.length);
     99         assertContains(events2[0], "WakeupStats");
    100         assertContains(events2[0], "wlan0");
    101         for (int i = 0; i < uids.length; i++) {
    102             String got = events2[i+1];
    103             assertContains(got, "WakeupEvent");
    104             assertContains(got, "wlan0");
    105             assertContains(got, "uid: " + uids[i]);
    106         }
    107 
    108         int uid = 20000;
    109         for (int i = 0; i < BUFFER_LENGTH * 2; i++) {
    110             long ts = now + 10;
    111             mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, ts);
    112         }
    113 
    114         String[] events3 = listNetdEvent();
    115         int expectedLength3 = BUFFER_LENGTH + 1; // +1 for the WakeupStats line
    116         assertEquals(expectedLength3, events3.length);
    117         assertContains(events2[0], "WakeupStats");
    118         assertContains(events2[0], "wlan0");
    119         for (int i = 1; i < expectedLength3; i++) {
    120             String got = events3[i];
    121             assertContains(got, "WakeupEvent");
    122             assertContains(got, "wlan0");
    123             assertContains(got, "uid: " + uid);
    124         }
    125 
    126         uid = 45678;
    127         mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
    128 
    129         String[] events4 = listNetdEvent();
    130         String lastEvent = events4[events4.length - 1];
    131         assertContains(lastEvent, "WakeupEvent");
    132         assertContains(lastEvent, "wlan0");
    133         assertContains(lastEvent, "uid: " + uid);
    134     }
    135 
    136     @Test
    137     public void testWakeupStatsLogging() throws Exception {
    138         wakeupEvent("wlan0", 1000);
    139         wakeupEvent("rmnet0", 10123);
    140         wakeupEvent("wlan0", 1000);
    141         wakeupEvent("rmnet0", 10008);
    142         wakeupEvent("wlan0", -1);
    143         wakeupEvent("wlan0", 10008);
    144         wakeupEvent("rmnet0", 1000);
    145         wakeupEvent("wlan0", 10004);
    146         wakeupEvent("wlan0", 1000);
    147         wakeupEvent("wlan0", 0);
    148         wakeupEvent("wlan0", -1);
    149         wakeupEvent("rmnet0", 10052);
    150         wakeupEvent("wlan0", 0);
    151         wakeupEvent("rmnet0", 1000);
    152         wakeupEvent("wlan0", 1010);
    153 
    154         String got = flushStatistics();
    155         String want = String.join("\n",
    156                 "dropped_events: 0",
    157                 "events <",
    158                 "  if_name: \"\"",
    159                 "  link_layer: 2",
    160                 "  network_id: 0",
    161                 "  time_ms: 0",
    162                 "  transports: 0",
    163                 "  wakeup_stats <",
    164                 "    application_wakeups: 3",
    165                 "    duration_sec: 0",
    166                 "    no_uid_wakeups: 0",
    167                 "    non_application_wakeups: 0",
    168                 "    root_wakeups: 0",
    169                 "    system_wakeups: 2",
    170                 "    total_wakeups: 5",
    171                 "  >",
    172                 ">",
    173                 "events <",
    174                 "  if_name: \"\"",
    175                 "  link_layer: 4",
    176                 "  network_id: 0",
    177                 "  time_ms: 0",
    178                 "  transports: 0",
    179                 "  wakeup_stats <",
    180                 "    application_wakeups: 2",
    181                 "    duration_sec: 0",
    182                 "    no_uid_wakeups: 2",
    183                 "    non_application_wakeups: 1",
    184                 "    root_wakeups: 2",
    185                 "    system_wakeups: 3",
    186                 "    total_wakeups: 10",
    187                 "  >",
    188                 ">",
    189                 "version: 2\n");
    190         assertEquals(want, got);
    191     }
    192 
    193     @Test
    194     public void testDnsLogging() throws Exception {
    195         asyncDump(100);
    196 
    197         dnsEvent(100, EVENT_GETADDRINFO, 0, 3456);
    198         dnsEvent(100, EVENT_GETADDRINFO, 0, 267);
    199         dnsEvent(100, EVENT_GETHOSTBYNAME, 22, 1230);
    200         dnsEvent(100, EVENT_GETADDRINFO, 3, 45);
    201         dnsEvent(100, EVENT_GETADDRINFO, 1, 2111);
    202         dnsEvent(100, EVENT_GETADDRINFO, 0, 450);
    203         dnsEvent(100, EVENT_GETHOSTBYNAME, 200, 638);
    204         dnsEvent(100, EVENT_GETHOSTBYNAME, 178, 1300);
    205         dnsEvent(101, EVENT_GETADDRINFO, 0, 56);
    206         dnsEvent(101, EVENT_GETADDRINFO, 0, 78);
    207         dnsEvent(101, EVENT_GETADDRINFO, 0, 14);
    208         dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 56);
    209         dnsEvent(101, EVENT_GETADDRINFO, 0, 78);
    210         dnsEvent(101, EVENT_GETADDRINFO, 0, 14);
    211 
    212         String got = flushStatistics();
    213         String want = String.join("\n",
    214                 "dropped_events: 0",
    215                 "events <",
    216                 "  if_name: \"\"",
    217                 "  link_layer: 4",
    218                 "  network_id: 100",
    219                 "  time_ms: 0",
    220                 "  transports: 2",
    221                 "  dns_lookup_batch <",
    222                 "    event_types: 1",
    223                 "    event_types: 1",
    224                 "    event_types: 2",
    225                 "    event_types: 1",
    226                 "    event_types: 1",
    227                 "    event_types: 1",
    228                 "    event_types: 2",
    229                 "    event_types: 2",
    230                 "    getaddrinfo_error_count: 0",
    231                 "    getaddrinfo_query_count: 0",
    232                 "    gethostbyname_error_count: 0",
    233                 "    gethostbyname_query_count: 0",
    234                 "    latencies_ms: 3456",
    235                 "    latencies_ms: 267",
    236                 "    latencies_ms: 1230",
    237                 "    latencies_ms: 45",
    238                 "    latencies_ms: 2111",
    239                 "    latencies_ms: 450",
    240                 "    latencies_ms: 638",
    241                 "    latencies_ms: 1300",
    242                 "    return_codes: 0",
    243                 "    return_codes: 0",
    244                 "    return_codes: 22",
    245                 "    return_codes: 3",
    246                 "    return_codes: 1",
    247                 "    return_codes: 0",
    248                 "    return_codes: 200",
    249                 "    return_codes: 178",
    250                 "  >",
    251                 ">",
    252                 "events <",
    253                 "  if_name: \"\"",
    254                 "  link_layer: 2",
    255                 "  network_id: 101",
    256                 "  time_ms: 0",
    257                 "  transports: 1",
    258                 "  dns_lookup_batch <",
    259                 "    event_types: 1",
    260                 "    event_types: 1",
    261                 "    event_types: 1",
    262                 "    event_types: 2",
    263                 "    event_types: 1",
    264                 "    event_types: 1",
    265                 "    getaddrinfo_error_count: 0",
    266                 "    getaddrinfo_query_count: 0",
    267                 "    gethostbyname_error_count: 0",
    268                 "    gethostbyname_query_count: 0",
    269                 "    latencies_ms: 56",
    270                 "    latencies_ms: 78",
    271                 "    latencies_ms: 14",
    272                 "    latencies_ms: 56",
    273                 "    latencies_ms: 78",
    274                 "    latencies_ms: 14",
    275                 "    return_codes: 0",
    276                 "    return_codes: 0",
    277                 "    return_codes: 0",
    278                 "    return_codes: 0",
    279                 "    return_codes: 0",
    280                 "    return_codes: 0",
    281                 "  >",
    282                 ">",
    283                 "version: 2\n");
    284         assertEquals(want, got);
    285     }
    286 
    287     @Test
    288     public void testConnectLogging() throws Exception {
    289         asyncDump(100);
    290 
    291         final int OK = 0;
    292         Thread[] logActions = {
    293             // ignored
    294             connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4),
    295             connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV6),
    296             connectEventAction(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4),
    297             connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
    298             connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
    299             // valid latencies
    300             connectEventAction(100, OK, 110, EXAMPLE_IPV4),
    301             connectEventAction(100, OK, 23, EXAMPLE_IPV4),
    302             connectEventAction(100, OK, 45, EXAMPLE_IPV4),
    303             connectEventAction(101, OK, 56, EXAMPLE_IPV4),
    304             connectEventAction(101, OK, 523, EXAMPLE_IPV6),
    305             connectEventAction(101, OK, 214, EXAMPLE_IPV6),
    306             connectEventAction(101, OK, 67, EXAMPLE_IPV6),
    307             // errors
    308             connectEventAction(100, OsConstants.EPERM, 0, EXAMPLE_IPV4),
    309             connectEventAction(101, OsConstants.EPERM, 0, EXAMPLE_IPV4),
    310             connectEventAction(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4),
    311             connectEventAction(100, OsConstants.EACCES, 0, EXAMPLE_IPV4),
    312             connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV4),
    313             connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV6),
    314             connectEventAction(100, OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4),
    315             connectEventAction(101, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4),
    316             connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
    317             connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
    318             connectEventAction(101, OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4),
    319         };
    320 
    321         for (Thread t : logActions) {
    322             t.start();
    323         }
    324         for (Thread t : logActions) {
    325             t.join();
    326         }
    327 
    328         String got = flushStatistics();
    329         String want = String.join("\n",
    330                 "dropped_events: 0",
    331                 "events <",
    332                 "  if_name: \"\"",
    333                 "  link_layer: 4",
    334                 "  network_id: 100",
    335                 "  time_ms: 0",
    336                 "  transports: 2",
    337                 "  connect_statistics <",
    338                 "    connect_blocking_count: 3",
    339                 "    connect_count: 6",
    340                 "    errnos_counters <",
    341                 "      key: 1",
    342                 "      value: 1",
    343                 "    >",
    344                 "    errnos_counters <",
    345                 "      key: 11",
    346                 "      value: 1",
    347                 "    >",
    348                 "    errnos_counters <",
    349                 "      key: 13",
    350                 "      value: 1",
    351                 "    >",
    352                 "    errnos_counters <",
    353                 "      key: 98",
    354                 "      value: 1",
    355                 "    >",
    356                 "    errnos_counters <",
    357                 "      key: 110",
    358                 "      value: 2",
    359                 "    >",
    360                 "    ipv6_addr_count: 1",
    361                 "    latencies_ms: 23",
    362                 "    latencies_ms: 45",
    363                 "    latencies_ms: 110",
    364                 "  >",
    365                 ">",
    366                 "events <",
    367                 "  if_name: \"\"",
    368                 "  link_layer: 2",
    369                 "  network_id: 101",
    370                 "  time_ms: 0",
    371                 "  transports: 1",
    372                 "  connect_statistics <",
    373                 "    connect_blocking_count: 4",
    374                 "    connect_count: 6",
    375                 "    errnos_counters <",
    376                 "      key: 1",
    377                 "      value: 1",
    378                 "    >",
    379                 "    errnos_counters <",
    380                 "      key: 13",
    381                 "      value: 2",
    382                 "    >",
    383                 "    errnos_counters <",
    384                 "      key: 110",
    385                 "      value: 1",
    386                 "    >",
    387                 "    errnos_counters <",
    388                 "      key: 111",
    389                 "      value: 1",
    390                 "    >",
    391                 "    ipv6_addr_count: 5",
    392                 "    latencies_ms: 56",
    393                 "    latencies_ms: 67",
    394                 "    latencies_ms: 214",
    395                 "    latencies_ms: 523",
    396                 "  >",
    397                 ">",
    398                 "version: 2\n");
    399         assertEquals(want, got);
    400     }
    401 
    402     Thread connectEventAction(int netId, int error, int latencyMs, String ipAddr) {
    403         return new Thread(() -> {
    404             try {
    405                 mNetdEventListenerService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1);
    406             } catch (Exception e) {
    407                 fail(e.toString());
    408             }
    409         });
    410     }
    411 
    412     void dnsEvent(int netId, int type, int result, int latency) throws Exception {
    413         mNetdEventListenerService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
    414     }
    415 
    416     void wakeupEvent(String iface, int uid) throws Exception {
    417         String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
    418         mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, 0);
    419     }
    420 
    421     void asyncDump(long durationMs) throws Exception {
    422         final long stop = System.currentTimeMillis() + durationMs;
    423         final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
    424         new Thread(() -> {
    425             while (System.currentTimeMillis() < stop) {
    426                 mNetdEventListenerService.dump(pw);
    427             }
    428         }).start();
    429     }
    430 
    431     // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
    432     String flushStatistics() throws Exception {
    433         IpConnectivityMetrics metricsService =
    434                 new IpConnectivityMetrics(mock(Context.class), (ctx) -> 2000);
    435         metricsService.mNetdListener = mNetdEventListenerService;
    436 
    437         StringWriter buffer = new StringWriter();
    438         PrintWriter writer = new PrintWriter(buffer);
    439         metricsService.impl.dump(null, writer, new String[]{"flush"});
    440         byte[] bytes = Base64.decode(buffer.toString(), Base64.DEFAULT);
    441         IpConnectivityLog log = IpConnectivityLog.parseFrom(bytes);
    442         for (IpConnectivityEvent ev : log.events) {
    443             if (ev.getConnectStatistics() == null) {
    444                 continue;
    445             }
    446             // Sort repeated fields of connect() events arriving in non-deterministic order.
    447             Arrays.sort(ev.getConnectStatistics().latenciesMs);
    448             Arrays.sort(ev.getConnectStatistics().errnosCounters,
    449                     Comparator.comparingInt((p) -> p.key));
    450         }
    451         return log.toString();
    452     }
    453 
    454     String[] listNetdEvent() throws Exception {
    455         StringWriter buffer = new StringWriter();
    456         PrintWriter writer = new PrintWriter(buffer);
    457         mNetdEventListenerService.list(writer);
    458         return buffer.toString().split("\\n");
    459     }
    460 
    461     static void assertContains(String got, String want) {
    462         assertTrue(got + " did not contain \"" + want + "\"", got.contains(want));
    463     }
    464 }
    465