Home | History | Annotate | Download | only in hostside
      1 /*
      2  * Copyright (C) 2014 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.cts.net.hostside;
     18 
     19 import static android.system.OsConstants.*;
     20 
     21 import android.content.Intent;
     22 import android.content.pm.PackageManager;
     23 import android.net.ConnectivityManager;
     24 import android.net.ConnectivityManager.NetworkCallback;
     25 import android.net.LinkProperties;
     26 import android.net.Network;
     27 import android.net.NetworkCapabilities;
     28 import android.net.NetworkRequest;
     29 import android.net.VpnService;
     30 import android.os.ParcelFileDescriptor;
     31 import android.os.Process;
     32 import android.support.test.uiautomator.UiDevice;
     33 import android.support.test.uiautomator.UiObject;
     34 import android.support.test.uiautomator.UiObjectNotFoundException;
     35 import android.support.test.uiautomator.UiScrollable;
     36 import android.support.test.uiautomator.UiSelector;
     37 import android.system.ErrnoException;
     38 import android.system.Os;
     39 import android.system.StructPollfd;
     40 import android.test.InstrumentationTestCase;
     41 import android.test.MoreAsserts;
     42 import android.text.TextUtils;
     43 import android.util.Log;
     44 
     45 import com.android.cts.net.hostside.IRemoteSocketFactory;
     46 
     47 import java.io.BufferedReader;
     48 import java.io.Closeable;
     49 import java.io.FileDescriptor;
     50 import java.io.FileOutputStream;
     51 import java.io.FileInputStream;
     52 import java.io.InputStreamReader;
     53 import java.io.IOException;
     54 import java.io.InputStream;
     55 import java.io.OutputStream;
     56 import java.io.PrintWriter;
     57 import java.net.DatagramPacket;
     58 import java.net.DatagramSocket;
     59 import java.net.Inet6Address;
     60 import java.net.InetAddress;
     61 import java.net.InetSocketAddress;
     62 import java.net.ServerSocket;
     63 import java.net.Socket;
     64 import java.net.SocketException;
     65 import java.nio.charset.StandardCharsets;
     66 import java.util.Random;
     67 
     68 /**
     69  * Tests for the VpnService API.
     70  *
     71  * These tests establish a VPN via the VpnService API, and have the service reflect the packets back
     72  * to the device without causing any network traffic. This allows testing the local VPN data path
     73  * without a network connection or a VPN server.
     74  *
     75  * Note: in Lollipop, VPN functionality relies on kernel support for UID-based routing. If these
     76  * tests fail, it may be due to the lack of kernel support. The necessary patches can be
     77  * cherry-picked from the Android common kernel trees:
     78  *
     79  * android-3.10:
     80  *   https://android-review.googlesource.com/#/c/99220/
     81  *   https://android-review.googlesource.com/#/c/100545/
     82  *
     83  * android-3.4:
     84  *   https://android-review.googlesource.com/#/c/99225/
     85  *   https://android-review.googlesource.com/#/c/100557/
     86  *
     87  * To ensure that the kernel has the required commits, run the kernel unit
     88  * tests described at:
     89  *
     90  *   https://source.android.com/devices/tech/config/kernel_network_tests.html
     91  *
     92  */
     93 public class VpnTest extends InstrumentationTestCase {
     94 
     95     public static String TAG = "VpnTest";
     96     public static int TIMEOUT_MS = 3 * 1000;
     97     public static int SOCKET_TIMEOUT_MS = 100;
     98     public static String TEST_HOST = "connectivitycheck.gstatic.com";
     99 
    100     private UiDevice mDevice;
    101     private MyActivity mActivity;
    102     private String mPackageName;
    103     private ConnectivityManager mCM;
    104     private RemoteSocketFactoryClient mRemoteSocketFactoryClient;
    105 
    106     Network mNetwork;
    107     NetworkCallback mCallback;
    108     final Object mLock = new Object();
    109     final Object mLockShutdown = new Object();
    110 
    111     private boolean supportedHardware() {
    112         final PackageManager pm = getInstrumentation().getContext().getPackageManager();
    113         return !pm.hasSystemFeature("android.hardware.type.watch");
    114     }
    115 
    116     @Override
    117     public void setUp() throws Exception {
    118         super.setUp();
    119 
    120         mNetwork = null;
    121         mCallback = null;
    122 
    123         mDevice = UiDevice.getInstance(getInstrumentation());
    124         mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
    125                 MyActivity.class, null);
    126         mPackageName = mActivity.getPackageName();
    127         mCM = (ConnectivityManager) mActivity.getSystemService(mActivity.CONNECTIVITY_SERVICE);
    128         mRemoteSocketFactoryClient = new RemoteSocketFactoryClient(mActivity);
    129         mRemoteSocketFactoryClient.bind();
    130         mDevice.waitForIdle();
    131     }
    132 
    133     @Override
    134     public void tearDown() throws Exception {
    135         mRemoteSocketFactoryClient.unbind();
    136         if (mCallback != null) {
    137             mCM.unregisterNetworkCallback(mCallback);
    138         }
    139         Log.i(TAG, "Stopping VPN");
    140         stopVpn();
    141         mActivity.finish();
    142         super.tearDown();
    143     }
    144 
    145     private void prepareVpn() throws Exception {
    146         final int REQUEST_ID = 42;
    147 
    148         // Attempt to prepare.
    149         Log.i(TAG, "Preparing VPN");
    150         Intent intent = VpnService.prepare(mActivity);
    151 
    152         if (intent != null) {
    153             // Start the confirmation dialog and click OK.
    154             mActivity.startActivityForResult(intent, REQUEST_ID);
    155             mDevice.waitForIdle();
    156 
    157             String packageName = intent.getComponent().getPackageName();
    158             String resourceIdRegex = "android:id/button1$|button_start_vpn";
    159             final UiObject okButton = new UiObject(new UiSelector()
    160                     .className("android.widget.Button")
    161                     .packageName(packageName)
    162                     .resourceIdMatches(resourceIdRegex));
    163             if (okButton.waitForExists(TIMEOUT_MS) == false) {
    164                 mActivity.finishActivity(REQUEST_ID);
    165                 fail("VpnService.prepare returned an Intent for '" + intent.getComponent() + "' " +
    166                      "to display the VPN confirmation dialog, but this test could not find the " +
    167                      "button to allow the VPN application to connect. Please ensure that the "  +
    168                      "component displays a button with a resource ID matching the regexp: '" +
    169                      resourceIdRegex + "'.");
    170             }
    171 
    172             // Click the button and wait for RESULT_OK.
    173             okButton.click();
    174             try {
    175                 int result = mActivity.getResult(TIMEOUT_MS);
    176                 if (result != MyActivity.RESULT_OK) {
    177                     fail("The VPN confirmation dialog did not return RESULT_OK when clicking on " +
    178                          "the button matching the regular expression '" + resourceIdRegex +
    179                          "' of " + intent.getComponent() + "'. Please ensure that clicking on " +
    180                          "that button allows the VPN application to connect. " +
    181                          "Return value: " + result);
    182                 }
    183             } catch (InterruptedException e) {
    184                 fail("VPN confirmation dialog did not return after " + TIMEOUT_MS + "ms");
    185             }
    186 
    187             // Now we should be prepared.
    188             intent = VpnService.prepare(mActivity);
    189             if (intent != null) {
    190                 fail("VpnService.prepare returned non-null even after the VPN dialog " +
    191                      intent.getComponent() + "returned RESULT_OK.");
    192             }
    193         }
    194     }
    195 
    196     private void startVpn(
    197             String[] addresses, String[] routes,
    198             String allowedApplications, String disallowedApplications) throws Exception {
    199 
    200         prepareVpn();
    201 
    202         // Register a callback so we will be notified when our VPN comes up.
    203         final NetworkRequest request = new NetworkRequest.Builder()
    204                 .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
    205                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
    206                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    207                 .build();
    208         mCallback = new NetworkCallback() {
    209             public void onAvailable(Network network) {
    210                 synchronized (mLock) {
    211                     Log.i(TAG, "Got available callback for network=" + network);
    212                     mNetwork = network;
    213                     mLock.notify();
    214                 }
    215             }
    216         };
    217         mCM.registerNetworkCallback(request, mCallback);  // Unregistered in tearDown.
    218 
    219         // Start the service and wait up for TIMEOUT_MS ms for the VPN to come up.
    220         Intent intent = new Intent(mActivity, MyVpnService.class)
    221                 .putExtra(mPackageName + ".cmd", "connect")
    222                 .putExtra(mPackageName + ".addresses", TextUtils.join(",", addresses))
    223                 .putExtra(mPackageName + ".routes", TextUtils.join(",", routes))
    224                 .putExtra(mPackageName + ".allowedapplications", allowedApplications)
    225                 .putExtra(mPackageName + ".disallowedapplications", disallowedApplications);
    226         mActivity.startService(intent);
    227         synchronized (mLock) {
    228             if (mNetwork == null) {
    229                  Log.i(TAG, "bf mLock");
    230                  mLock.wait(TIMEOUT_MS);
    231                  Log.i(TAG, "af mLock");
    232             }
    233         }
    234 
    235         if (mNetwork == null) {
    236             fail("VPN did not become available after " + TIMEOUT_MS + "ms");
    237         }
    238 
    239         // Unfortunately, when the available callback fires, the VPN UID ranges are not yet
    240         // configured. Give the system some time to do so. http://b/18436087 .
    241         try { Thread.sleep(3000); } catch(InterruptedException e) {}
    242     }
    243 
    244     private void stopVpn() {
    245         // Register a callback so we will be notified when our VPN comes up.
    246         final NetworkRequest request = new NetworkRequest.Builder()
    247                 .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
    248                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
    249                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    250                 .build();
    251         mCallback = new NetworkCallback() {
    252             public void onLost(Network network) {
    253                 synchronized (mLockShutdown) {
    254                     Log.i(TAG, "Got lost callback for network=" + network + ",mNetwork = " + mNetwork);
    255                     if( mNetwork == network){
    256                         mLockShutdown.notify();
    257                     }
    258                 }
    259             }
    260        };
    261         mCM.registerNetworkCallback(request, mCallback);  // Unregistered in tearDown.
    262         // Simply calling mActivity.stopService() won't stop the service, because the system binds
    263         // to the service for the purpose of sending it a revoke command if another VPN comes up,
    264         // and stopping a bound service has no effect. Instead, "start" the service again with an
    265         // Intent that tells it to disconnect.
    266         Intent intent = new Intent(mActivity, MyVpnService.class)
    267                 .putExtra(mPackageName + ".cmd", "disconnect");
    268         mActivity.startService(intent);
    269         synchronized (mLockShutdown) {
    270             try {
    271                  Log.i(TAG, "bf mLockShutdown");
    272                  mLockShutdown.wait(TIMEOUT_MS);
    273                  Log.i(TAG, "af mLockShutdown");
    274             } catch(InterruptedException e) {}
    275         }
    276     }
    277 
    278     private static void closeQuietly(Closeable c) {
    279         if (c != null) {
    280             try {
    281                 c.close();
    282             } catch (IOException e) {
    283             }
    284         }
    285     }
    286 
    287     private static void checkPing(String to) throws IOException, ErrnoException {
    288         InetAddress address = InetAddress.getByName(to);
    289         FileDescriptor s;
    290         final int LENGTH = 64;
    291         byte[] packet = new byte[LENGTH];
    292         byte[] header;
    293 
    294         // Construct a ping packet.
    295         Random random = new Random();
    296         random.nextBytes(packet);
    297         if (address instanceof Inet6Address) {
    298             s = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
    299             header = new byte[] { (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
    300         } else {
    301             // Note that this doesn't actually work due to http://b/18558481 .
    302             s = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
    303             header = new byte[] { (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
    304         }
    305         System.arraycopy(header, 0, packet, 0, header.length);
    306 
    307         // Send the packet.
    308         int port = random.nextInt(65534) + 1;
    309         Os.connect(s, address, port);
    310         Os.write(s, packet, 0, packet.length);
    311 
    312         // Expect a reply.
    313         StructPollfd pollfd = new StructPollfd();
    314         pollfd.events = (short) POLLIN;  // "error: possible loss of precision"
    315         pollfd.fd = s;
    316         int ret = Os.poll(new StructPollfd[] { pollfd }, SOCKET_TIMEOUT_MS);
    317         assertEquals("Expected reply after sending ping", 1, ret);
    318 
    319         byte[] reply = new byte[LENGTH];
    320         int read = Os.read(s, reply, 0, LENGTH);
    321         assertEquals(LENGTH, read);
    322 
    323         // Find out what the kernel set the ICMP ID to.
    324         InetSocketAddress local = (InetSocketAddress) Os.getsockname(s);
    325         port = local.getPort();
    326         packet[4] = (byte) ((port >> 8) & 0xff);
    327         packet[5] = (byte) (port & 0xff);
    328 
    329         // Check the contents.
    330         if (packet[0] == (byte) 0x80) {
    331             packet[0] = (byte) 0x81;
    332         } else {
    333             packet[0] = 0;
    334         }
    335         // Zero out the checksum in the reply so it matches the uninitialized checksum in packet.
    336         reply[2] = reply[3] = 0;
    337         MoreAsserts.assertEquals(packet, reply);
    338     }
    339 
    340     // Writes data to out and checks that it appears identically on in.
    341     private static void writeAndCheckData(
    342             OutputStream out, InputStream in, byte[] data) throws IOException {
    343         out.write(data, 0, data.length);
    344         out.flush();
    345 
    346         byte[] read = new byte[data.length];
    347         int bytesRead = 0, totalRead = 0;
    348         do {
    349             bytesRead = in.read(read, totalRead, read.length - totalRead);
    350             totalRead += bytesRead;
    351         } while (bytesRead >= 0 && totalRead < data.length);
    352         assertEquals(totalRead, data.length);
    353         MoreAsserts.assertEquals(data, read);
    354     }
    355 
    356     private static void checkTcpReflection(String to, String expectedFrom) throws IOException {
    357         // Exercise TCP over the VPN by "connecting to ourselves". We open a server socket and a
    358         // client socket, and connect the client socket to a remote host, with the port of the
    359         // server socket. The PacketReflector reflects the packets, changing the source addresses
    360         // but not the ports, so our client socket is connected to our server socket, though both
    361         // sockets think their peers are on the "remote" IP address.
    362 
    363         // Open a listening socket.
    364         ServerSocket listen = new ServerSocket(0, 10, InetAddress.getByName("::"));
    365 
    366         // Connect the client socket to it.
    367         InetAddress toAddr = InetAddress.getByName(to);
    368         Socket client = new Socket();
    369         try {
    370             client.connect(new InetSocketAddress(toAddr, listen.getLocalPort()), SOCKET_TIMEOUT_MS);
    371             if (expectedFrom == null) {
    372                 closeQuietly(listen);
    373                 closeQuietly(client);
    374                 fail("Expected connection to fail, but it succeeded.");
    375             }
    376         } catch (IOException e) {
    377             if (expectedFrom != null) {
    378                 closeQuietly(listen);
    379                 fail("Expected connection to succeed, but it failed.");
    380             } else {
    381                 // We expected the connection to fail, and it did, so there's nothing more to test.
    382                 return;
    383             }
    384         }
    385 
    386         // The connection succeeded, and we expected it to succeed. Send some data; if things are
    387         // working, the data will be sent to the VPN, reflected by the PacketReflector, and arrive
    388         // at our server socket. For good measure, send some data in the other direction.
    389         Socket server = null;
    390         try {
    391             // Accept the connection on the server side.
    392             listen.setSoTimeout(SOCKET_TIMEOUT_MS);
    393             server = listen.accept();
    394 
    395             // Check that the source and peer addresses are as expected.
    396             assertEquals(expectedFrom, client.getLocalAddress().getHostAddress());
    397             assertEquals(expectedFrom, server.getLocalAddress().getHostAddress());
    398             assertEquals(
    399                     new InetSocketAddress(toAddr, client.getLocalPort()),
    400                     server.getRemoteSocketAddress());
    401             assertEquals(
    402                     new InetSocketAddress(toAddr, server.getLocalPort()),
    403                     client.getRemoteSocketAddress());
    404 
    405             // Now write some data.
    406             final int LENGTH = 32768;
    407             byte[] data = new byte[LENGTH];
    408             new Random().nextBytes(data);
    409 
    410             // Make sure our writes don't block or time out, because we're single-threaded and can't
    411             // read and write at the same time.
    412             server.setReceiveBufferSize(LENGTH * 2);
    413             client.setSendBufferSize(LENGTH * 2);
    414             client.setSoTimeout(SOCKET_TIMEOUT_MS);
    415             server.setSoTimeout(SOCKET_TIMEOUT_MS);
    416 
    417             // Send some data from client to server, then from server to client.
    418             writeAndCheckData(client.getOutputStream(), server.getInputStream(), data);
    419             writeAndCheckData(server.getOutputStream(), client.getInputStream(), data);
    420         } finally {
    421             closeQuietly(listen);
    422             closeQuietly(client);
    423             closeQuietly(server);
    424         }
    425     }
    426 
    427     private static void checkUdpEcho(String to, String expectedFrom) throws IOException {
    428         DatagramSocket s;
    429         InetAddress address = InetAddress.getByName(to);
    430         if (address instanceof Inet6Address) {  // http://b/18094870
    431             s = new DatagramSocket(0, InetAddress.getByName("::"));
    432         } else {
    433             s = new DatagramSocket();
    434         }
    435         s.setSoTimeout(SOCKET_TIMEOUT_MS);
    436 
    437         Random random = new Random();
    438         byte[] data = new byte[random.nextInt(1650)];
    439         random.nextBytes(data);
    440         DatagramPacket p = new DatagramPacket(data, data.length);
    441         s.connect(address, 7);
    442 
    443         if (expectedFrom != null) {
    444             assertEquals("Unexpected source address: ",
    445                          expectedFrom, s.getLocalAddress().getHostAddress());
    446         }
    447 
    448         try {
    449             if (expectedFrom != null) {
    450                 s.send(p);
    451                 s.receive(p);
    452                 MoreAsserts.assertEquals(data, p.getData());
    453             } else {
    454                 try {
    455                     s.send(p);
    456                     s.receive(p);
    457                     fail("Received unexpected reply");
    458                 } catch(IOException expected) {}
    459             }
    460         } finally {
    461             s.close();
    462         }
    463     }
    464 
    465     private void checkTrafficOnVpn() throws Exception {
    466         checkUdpEcho("192.0.2.251", "192.0.2.2");
    467         checkUdpEcho("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
    468         checkPing("2001:db8:dead:beef::f00");
    469         checkTcpReflection("192.0.2.252", "192.0.2.2");
    470         checkTcpReflection("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
    471     }
    472 
    473     private void checkNoTrafficOnVpn() throws Exception {
    474         checkUdpEcho("192.0.2.251", null);
    475         checkUdpEcho("2001:db8:dead:beef::f00", null);
    476         checkTcpReflection("192.0.2.252", null);
    477         checkTcpReflection("2001:db8:dead:beef::f00", null);
    478     }
    479 
    480     private FileDescriptor openSocketFd(String host, int port, int timeoutMs) throws Exception {
    481         Socket s = new Socket(host, port);
    482         s.setSoTimeout(timeoutMs);
    483         // Dup the filedescriptor so ParcelFileDescriptor's finalizer doesn't garbage collect it
    484         // and cause our fd to become invalid. http://b/35927643 .
    485         FileDescriptor fd = Os.dup(ParcelFileDescriptor.fromSocket(s).getFileDescriptor());
    486         s.close();
    487         return fd;
    488     }
    489 
    490     private FileDescriptor openSocketFdInOtherApp(
    491             String host, int port, int timeoutMs) throws Exception {
    492         Log.d(TAG, String.format("Creating test socket in UID=%d, my UID=%d",
    493                 mRemoteSocketFactoryClient.getUid(), Os.getuid()));
    494         FileDescriptor fd = mRemoteSocketFactoryClient.openSocketFd(host, port, TIMEOUT_MS);
    495         return fd;
    496     }
    497 
    498     private void sendRequest(FileDescriptor fd, String host) throws Exception {
    499         String request = "GET /generate_204 HTTP/1.1\r\n" +
    500                 "Host: " + host + "\r\n" +
    501                 "Connection: keep-alive\r\n\r\n";
    502         byte[] requestBytes = request.getBytes(StandardCharsets.UTF_8);
    503         int ret = Os.write(fd, requestBytes, 0, requestBytes.length);
    504         Log.d(TAG, "Wrote " + ret + "bytes");
    505 
    506         String expected = "HTTP/1.1 204 No Content\r\n";
    507         byte[] response = new byte[expected.length()];
    508         Os.read(fd, response, 0, response.length);
    509 
    510         String actual = new String(response, StandardCharsets.UTF_8);
    511         assertEquals(expected, actual);
    512         Log.d(TAG, "Got response: " + actual);
    513     }
    514 
    515     private void assertSocketStillOpen(FileDescriptor fd, String host) throws Exception {
    516         try {
    517             assertTrue(fd.valid());
    518             sendRequest(fd, host);
    519             assertTrue(fd.valid());
    520         } finally {
    521             Os.close(fd);
    522         }
    523     }
    524 
    525     private void assertSocketClosed(FileDescriptor fd, String host) throws Exception {
    526         try {
    527             assertTrue(fd.valid());
    528             sendRequest(fd, host);
    529             fail("Socket opened before VPN connects should be closed when VPN connects");
    530         } catch (ErrnoException expected) {
    531             assertEquals(ECONNABORTED, expected.errno);
    532             assertTrue(fd.valid());
    533         } finally {
    534             Os.close(fd);
    535         }
    536     }
    537 
    538     public void testDefault() throws Exception {
    539         if (!supportedHardware()) return;
    540 
    541         FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
    542 
    543         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
    544                  new String[] {"0.0.0.0/0", "::/0"},
    545                  "", "");
    546 
    547         assertSocketClosed(fd, TEST_HOST);
    548 
    549         checkTrafficOnVpn();
    550     }
    551 
    552     public void testAppAllowed() throws Exception {
    553         if (!supportedHardware()) return;
    554 
    555         FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
    556 
    557         String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
    558         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
    559                  new String[] {"192.0.2.0/24", "2001:db8::/32"},
    560                  allowedApps, "");
    561 
    562         assertSocketClosed(fd, TEST_HOST);
    563 
    564         checkTrafficOnVpn();
    565     }
    566 
    567     public void testAppDisallowed() throws Exception {
    568         if (!supportedHardware()) return;
    569 
    570         FileDescriptor localFd = openSocketFd(TEST_HOST, 80, TIMEOUT_MS);
    571         FileDescriptor remoteFd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
    572 
    573         String disallowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
    574         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
    575                  new String[] {"192.0.2.0/24", "2001:db8::/32"},
    576                  "", disallowedApps);
    577 
    578         assertSocketStillOpen(localFd, TEST_HOST);
    579         assertSocketStillOpen(remoteFd, TEST_HOST);
    580 
    581         checkNoTrafficOnVpn();
    582     }
    583 }
    584