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.Manifest.permission.NETWORK_SETTINGS; 20 import static android.net.ConnectivityManager.TYPE_VPN; 21 import static android.net.NetworkCapabilities.TRANSPORT_VPN; 22 import static android.os.Process.INVALID_UID; 23 import static android.system.OsConstants.AF_INET; 24 import static android.system.OsConstants.AF_INET6; 25 import static android.system.OsConstants.ECONNABORTED; 26 import static android.system.OsConstants.IPPROTO_ICMP; 27 import static android.system.OsConstants.IPPROTO_ICMPV6; 28 import static android.system.OsConstants.IPPROTO_TCP; 29 import static android.system.OsConstants.POLLIN; 30 import static android.system.OsConstants.SOCK_DGRAM; 31 import static android.test.MoreAsserts.assertNotEqual; 32 33 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; 34 35 import android.annotation.Nullable; 36 import android.app.DownloadManager; 37 import android.app.DownloadManager.Query; 38 import android.app.DownloadManager.Request; 39 import android.content.BroadcastReceiver; 40 import android.content.ContentResolver; 41 import android.content.Context; 42 import android.content.Intent; 43 import android.content.IntentFilter; 44 import android.content.pm.PackageManager; 45 import android.database.Cursor; 46 import android.net.ConnectivityManager; 47 import android.net.ConnectivityManager.NetworkCallback; 48 import android.net.LinkProperties; 49 import android.net.Network; 50 import android.net.NetworkCapabilities; 51 import android.net.NetworkRequest; 52 import android.net.Proxy; 53 import android.net.ProxyInfo; 54 import android.net.TransportInfo; 55 import android.net.Uri; 56 import android.net.VpnManager; 57 import android.net.VpnService; 58 import android.net.VpnTransportInfo; 59 import android.net.wifi.WifiManager; 60 import android.os.Handler; 61 import android.os.Looper; 62 import android.os.ParcelFileDescriptor; 63 import android.os.Process; 64 import android.os.SystemProperties; 65 import android.os.UserHandle; 66 import android.provider.Settings; 67 import android.support.test.uiautomator.UiDevice; 68 import android.support.test.uiautomator.UiObject; 69 import android.support.test.uiautomator.UiSelector; 70 import android.system.ErrnoException; 71 import android.system.Os; 72 import android.system.OsConstants; 73 import android.system.StructPollfd; 74 import android.test.InstrumentationTestCase; 75 import android.test.MoreAsserts; 76 import android.text.TextUtils; 77 import android.util.Log; 78 79 import com.android.compatibility.common.util.BlockingBroadcastReceiver; 80 import com.android.modules.utils.build.SdkLevel; 81 import com.android.testutils.TestableNetworkCallback; 82 83 import java.io.Closeable; 84 import java.io.FileDescriptor; 85 import java.io.IOException; 86 import java.io.InputStream; 87 import java.io.OutputStream; 88 import java.net.DatagramPacket; 89 import java.net.DatagramSocket; 90 import java.net.Inet6Address; 91 import java.net.InetAddress; 92 import java.net.InetSocketAddress; 93 import java.net.ServerSocket; 94 import java.net.Socket; 95 import java.net.UnknownHostException; 96 import java.nio.charset.StandardCharsets; 97 import java.util.ArrayList; 98 import java.util.List; 99 import java.util.Objects; 100 import java.util.Random; 101 import java.util.concurrent.CompletableFuture; 102 import java.util.concurrent.CountDownLatch; 103 import java.util.concurrent.TimeUnit; 104 105 /** 106 * Tests for the VpnService API. 107 * 108 * These tests establish a VPN via the VpnService API, and have the service reflect the packets back 109 * to the device without causing any network traffic. This allows testing the local VPN data path 110 * without a network connection or a VPN server. 111 * 112 * Note: in Lollipop, VPN functionality relies on kernel support for UID-based routing. If these 113 * tests fail, it may be due to the lack of kernel support. The necessary patches can be 114 * cherry-picked from the Android common kernel trees: 115 * 116 * android-3.10: 117 * https://android-review.googlesource.com/#/c/99220/ 118 * https://android-review.googlesource.com/#/c/100545/ 119 * 120 * android-3.4: 121 * https://android-review.googlesource.com/#/c/99225/ 122 * https://android-review.googlesource.com/#/c/100557/ 123 * 124 * To ensure that the kernel has the required commits, run the kernel unit 125 * tests described at: 126 * 127 * https://source.android.com/devices/tech/config/kernel_network_tests.html 128 * 129 */ 130 public class VpnTest extends InstrumentationTestCase { 131 132 // These are neither public nor @TestApi. 133 // TODO: add them to @TestApi. 134 private static final String PRIVATE_DNS_MODE_SETTING = "private_dns_mode"; 135 private static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname"; 136 private static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic"; 137 private static final String PRIVATE_DNS_SPECIFIER_SETTING = "private_dns_specifier"; 138 139 public static String TAG = "VpnTest"; 140 public static int TIMEOUT_MS = 3 * 1000; 141 public static int SOCKET_TIMEOUT_MS = 100; 142 public static String TEST_HOST = "connectivitycheck.gstatic.com"; 143 144 private UiDevice mDevice; 145 private MyActivity mActivity; 146 private String mPackageName; 147 private ConnectivityManager mCM; 148 private WifiManager mWifiManager; 149 private RemoteSocketFactoryClient mRemoteSocketFactoryClient; 150 151 Network mNetwork; 152 NetworkCallback mCallback; 153 final Object mLock = new Object(); 154 final Object mLockShutdown = new Object(); 155 156 private String mOldPrivateDnsMode; 157 private String mOldPrivateDnsSpecifier; 158 supportedHardware()159 private boolean supportedHardware() { 160 final PackageManager pm = getInstrumentation().getContext().getPackageManager(); 161 return !pm.hasSystemFeature("android.hardware.type.watch"); 162 } 163 164 @Override setUp()165 public void setUp() throws Exception { 166 super.setUp(); 167 168 mNetwork = null; 169 mCallback = null; 170 storePrivateDnsSetting(); 171 172 mDevice = UiDevice.getInstance(getInstrumentation()); 173 mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(), 174 MyActivity.class, null); 175 mPackageName = mActivity.getPackageName(); 176 mCM = (ConnectivityManager) mActivity.getSystemService(Context.CONNECTIVITY_SERVICE); 177 mWifiManager = (WifiManager) mActivity.getSystemService(Context.WIFI_SERVICE); 178 mRemoteSocketFactoryClient = new RemoteSocketFactoryClient(mActivity); 179 mRemoteSocketFactoryClient.bind(); 180 mDevice.waitForIdle(); 181 } 182 183 @Override tearDown()184 public void tearDown() throws Exception { 185 restorePrivateDnsSetting(); 186 mRemoteSocketFactoryClient.unbind(); 187 if (mCallback != null) { 188 mCM.unregisterNetworkCallback(mCallback); 189 } 190 Log.i(TAG, "Stopping VPN"); 191 stopVpn(); 192 mActivity.finish(); 193 super.tearDown(); 194 } 195 prepareVpn()196 private void prepareVpn() throws Exception { 197 final int REQUEST_ID = 42; 198 199 // Attempt to prepare. 200 Log.i(TAG, "Preparing VPN"); 201 Intent intent = VpnService.prepare(mActivity); 202 203 if (intent != null) { 204 // Start the confirmation dialog and click OK. 205 mActivity.startActivityForResult(intent, REQUEST_ID); 206 mDevice.waitForIdle(); 207 208 String packageName = intent.getComponent().getPackageName(); 209 String resourceIdRegex = "android:id/button1$|button_start_vpn"; 210 final UiObject okButton = new UiObject(new UiSelector() 211 .className("android.widget.Button") 212 .packageName(packageName) 213 .resourceIdMatches(resourceIdRegex)); 214 if (okButton.waitForExists(TIMEOUT_MS) == false) { 215 mActivity.finishActivity(REQUEST_ID); 216 fail("VpnService.prepare returned an Intent for '" + intent.getComponent() + "' " + 217 "to display the VPN confirmation dialog, but this test could not find the " + 218 "button to allow the VPN application to connect. Please ensure that the " + 219 "component displays a button with a resource ID matching the regexp: '" + 220 resourceIdRegex + "'."); 221 } 222 223 // Click the button and wait for RESULT_OK. 224 okButton.click(); 225 try { 226 int result = mActivity.getResult(TIMEOUT_MS); 227 if (result != MyActivity.RESULT_OK) { 228 fail("The VPN confirmation dialog did not return RESULT_OK when clicking on " + 229 "the button matching the regular expression '" + resourceIdRegex + 230 "' of " + intent.getComponent() + "'. Please ensure that clicking on " + 231 "that button allows the VPN application to connect. " + 232 "Return value: " + result); 233 } 234 } catch (InterruptedException e) { 235 fail("VPN confirmation dialog did not return after " + TIMEOUT_MS + "ms"); 236 } 237 238 // Now we should be prepared. 239 intent = VpnService.prepare(mActivity); 240 if (intent != null) { 241 fail("VpnService.prepare returned non-null even after the VPN dialog " + 242 intent.getComponent() + "returned RESULT_OK."); 243 } 244 } 245 } 246 247 // TODO: Consider replacing arguments with a Builder. startVpn( String[] addresses, String[] routes, String allowedApplications, String disallowedApplications, @Nullable ProxyInfo proxyInfo, @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered)248 private void startVpn( 249 String[] addresses, String[] routes, String allowedApplications, 250 String disallowedApplications, @Nullable ProxyInfo proxyInfo, 251 @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered) throws Exception { 252 prepareVpn(); 253 254 // Register a callback so we will be notified when our VPN comes up. 255 final NetworkRequest request = new NetworkRequest.Builder() 256 .addTransportType(NetworkCapabilities.TRANSPORT_VPN) 257 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) 258 .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 259 .build(); 260 mCallback = new NetworkCallback() { 261 public void onAvailable(Network network) { 262 synchronized (mLock) { 263 Log.i(TAG, "Got available callback for network=" + network); 264 mNetwork = network; 265 mLock.notify(); 266 } 267 } 268 }; 269 mCM.registerNetworkCallback(request, mCallback); // Unregistered in tearDown. 270 271 // Start the service and wait up for TIMEOUT_MS ms for the VPN to come up. 272 Intent intent = new Intent(mActivity, MyVpnService.class) 273 .putExtra(mPackageName + ".cmd", "connect") 274 .putExtra(mPackageName + ".addresses", TextUtils.join(",", addresses)) 275 .putExtra(mPackageName + ".routes", TextUtils.join(",", routes)) 276 .putExtra(mPackageName + ".allowedapplications", allowedApplications) 277 .putExtra(mPackageName + ".disallowedapplications", disallowedApplications) 278 .putExtra(mPackageName + ".httpProxy", proxyInfo) 279 .putParcelableArrayListExtra( 280 mPackageName + ".underlyingNetworks", underlyingNetworks) 281 .putExtra(mPackageName + ".isAlwaysMetered", isAlwaysMetered); 282 283 mActivity.startService(intent); 284 synchronized (mLock) { 285 if (mNetwork == null) { 286 Log.i(TAG, "bf mLock"); 287 mLock.wait(TIMEOUT_MS); 288 Log.i(TAG, "af mLock"); 289 } 290 } 291 292 if (mNetwork == null) { 293 fail("VPN did not become available after " + TIMEOUT_MS + "ms"); 294 } 295 296 // Unfortunately, when the available callback fires, the VPN UID ranges are not yet 297 // configured. Give the system some time to do so. http://b/18436087 . 298 try { Thread.sleep(3000); } catch(InterruptedException e) {} 299 } 300 stopVpn()301 private void stopVpn() { 302 // Register a callback so we will be notified when our VPN comes up. 303 final NetworkRequest request = new NetworkRequest.Builder() 304 .addTransportType(NetworkCapabilities.TRANSPORT_VPN) 305 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) 306 .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 307 .build(); 308 mCallback = new NetworkCallback() { 309 public void onLost(Network network) { 310 synchronized (mLockShutdown) { 311 Log.i(TAG, "Got lost callback for network=" + network 312 + ",mNetwork = " + mNetwork); 313 if( mNetwork == network){ 314 mLockShutdown.notify(); 315 } 316 } 317 } 318 }; 319 mCM.registerNetworkCallback(request, mCallback); // Unregistered in tearDown. 320 // Simply calling mActivity.stopService() won't stop the service, because the system binds 321 // to the service for the purpose of sending it a revoke command if another VPN comes up, 322 // and stopping a bound service has no effect. Instead, "start" the service again with an 323 // Intent that tells it to disconnect. 324 Intent intent = new Intent(mActivity, MyVpnService.class) 325 .putExtra(mPackageName + ".cmd", "disconnect"); 326 mActivity.startService(intent); 327 synchronized (mLockShutdown) { 328 try { 329 Log.i(TAG, "bf mLockShutdown"); 330 mLockShutdown.wait(TIMEOUT_MS); 331 Log.i(TAG, "af mLockShutdown"); 332 } catch(InterruptedException e) {} 333 } 334 } 335 closeQuietly(Closeable c)336 private static void closeQuietly(Closeable c) { 337 if (c != null) { 338 try { 339 c.close(); 340 } catch (IOException e) { 341 } 342 } 343 } 344 checkPing(String to)345 private static void checkPing(String to) throws IOException, ErrnoException { 346 InetAddress address = InetAddress.getByName(to); 347 FileDescriptor s; 348 final int LENGTH = 64; 349 byte[] packet = new byte[LENGTH]; 350 byte[] header; 351 352 // Construct a ping packet. 353 Random random = new Random(); 354 random.nextBytes(packet); 355 if (address instanceof Inet6Address) { 356 s = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6); 357 header = new byte[] { (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; 358 } else { 359 // Note that this doesn't actually work due to http://b/18558481 . 360 s = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); 361 header = new byte[] { (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; 362 } 363 System.arraycopy(header, 0, packet, 0, header.length); 364 365 // Send the packet. 366 int port = random.nextInt(65534) + 1; 367 Os.connect(s, address, port); 368 Os.write(s, packet, 0, packet.length); 369 370 // Expect a reply. 371 StructPollfd pollfd = new StructPollfd(); 372 pollfd.events = (short) POLLIN; // "error: possible loss of precision" 373 pollfd.fd = s; 374 int ret = Os.poll(new StructPollfd[] { pollfd }, SOCKET_TIMEOUT_MS); 375 assertEquals("Expected reply after sending ping", 1, ret); 376 377 byte[] reply = new byte[LENGTH]; 378 int read = Os.read(s, reply, 0, LENGTH); 379 assertEquals(LENGTH, read); 380 381 // Find out what the kernel set the ICMP ID to. 382 InetSocketAddress local = (InetSocketAddress) Os.getsockname(s); 383 port = local.getPort(); 384 packet[4] = (byte) ((port >> 8) & 0xff); 385 packet[5] = (byte) (port & 0xff); 386 387 // Check the contents. 388 if (packet[0] == (byte) 0x80) { 389 packet[0] = (byte) 0x81; 390 } else { 391 packet[0] = 0; 392 } 393 // Zero out the checksum in the reply so it matches the uninitialized checksum in packet. 394 reply[2] = reply[3] = 0; 395 MoreAsserts.assertEquals(packet, reply); 396 } 397 398 // Writes data to out and checks that it appears identically on in. writeAndCheckData( OutputStream out, InputStream in, byte[] data)399 private static void writeAndCheckData( 400 OutputStream out, InputStream in, byte[] data) throws IOException { 401 out.write(data, 0, data.length); 402 out.flush(); 403 404 byte[] read = new byte[data.length]; 405 int bytesRead = 0, totalRead = 0; 406 do { 407 bytesRead = in.read(read, totalRead, read.length - totalRead); 408 totalRead += bytesRead; 409 } while (bytesRead >= 0 && totalRead < data.length); 410 assertEquals(totalRead, data.length); 411 MoreAsserts.assertEquals(data, read); 412 } 413 checkTcpReflection(String to, String expectedFrom)414 private void checkTcpReflection(String to, String expectedFrom) throws IOException { 415 // Exercise TCP over the VPN by "connecting to ourselves". We open a server socket and a 416 // client socket, and connect the client socket to a remote host, with the port of the 417 // server socket. The PacketReflector reflects the packets, changing the source addresses 418 // but not the ports, so our client socket is connected to our server socket, though both 419 // sockets think their peers are on the "remote" IP address. 420 421 // Open a listening socket. 422 ServerSocket listen = new ServerSocket(0, 10, InetAddress.getByName("::")); 423 424 // Connect the client socket to it. 425 InetAddress toAddr = InetAddress.getByName(to); 426 Socket client = new Socket(); 427 try { 428 client.connect(new InetSocketAddress(toAddr, listen.getLocalPort()), SOCKET_TIMEOUT_MS); 429 if (expectedFrom == null) { 430 closeQuietly(listen); 431 closeQuietly(client); 432 fail("Expected connection to fail, but it succeeded."); 433 } 434 } catch (IOException e) { 435 if (expectedFrom != null) { 436 closeQuietly(listen); 437 fail("Expected connection to succeed, but it failed."); 438 } else { 439 // We expected the connection to fail, and it did, so there's nothing more to test. 440 return; 441 } 442 } 443 444 // The connection succeeded, and we expected it to succeed. Send some data; if things are 445 // working, the data will be sent to the VPN, reflected by the PacketReflector, and arrive 446 // at our server socket. For good measure, send some data in the other direction. 447 Socket server = null; 448 try { 449 // Accept the connection on the server side. 450 listen.setSoTimeout(SOCKET_TIMEOUT_MS); 451 server = listen.accept(); 452 checkConnectionOwnerUidTcp(client); 453 checkConnectionOwnerUidTcp(server); 454 // Check that the source and peer addresses are as expected. 455 assertEquals(expectedFrom, client.getLocalAddress().getHostAddress()); 456 assertEquals(expectedFrom, server.getLocalAddress().getHostAddress()); 457 assertEquals( 458 new InetSocketAddress(toAddr, client.getLocalPort()), 459 server.getRemoteSocketAddress()); 460 assertEquals( 461 new InetSocketAddress(toAddr, server.getLocalPort()), 462 client.getRemoteSocketAddress()); 463 464 // Now write some data. 465 final int LENGTH = 32768; 466 byte[] data = new byte[LENGTH]; 467 new Random().nextBytes(data); 468 469 // Make sure our writes don't block or time out, because we're single-threaded and can't 470 // read and write at the same time. 471 server.setReceiveBufferSize(LENGTH * 2); 472 client.setSendBufferSize(LENGTH * 2); 473 client.setSoTimeout(SOCKET_TIMEOUT_MS); 474 server.setSoTimeout(SOCKET_TIMEOUT_MS); 475 476 // Send some data from client to server, then from server to client. 477 writeAndCheckData(client.getOutputStream(), server.getInputStream(), data); 478 writeAndCheckData(server.getOutputStream(), client.getInputStream(), data); 479 } finally { 480 closeQuietly(listen); 481 closeQuietly(client); 482 closeQuietly(server); 483 } 484 } 485 checkConnectionOwnerUidUdp(DatagramSocket s, boolean expectSuccess)486 private void checkConnectionOwnerUidUdp(DatagramSocket s, boolean expectSuccess) { 487 final int expectedUid = expectSuccess ? Process.myUid() : INVALID_UID; 488 InetSocketAddress loc = new InetSocketAddress(s.getLocalAddress(), s.getLocalPort()); 489 InetSocketAddress rem = new InetSocketAddress(s.getInetAddress(), s.getPort()); 490 int uid = mCM.getConnectionOwnerUid(OsConstants.IPPROTO_UDP, loc, rem); 491 assertEquals(expectedUid, uid); 492 } 493 checkConnectionOwnerUidTcp(Socket s)494 private void checkConnectionOwnerUidTcp(Socket s) { 495 final int expectedUid = Process.myUid(); 496 InetSocketAddress loc = new InetSocketAddress(s.getLocalAddress(), s.getLocalPort()); 497 InetSocketAddress rem = new InetSocketAddress(s.getInetAddress(), s.getPort()); 498 int uid = mCM.getConnectionOwnerUid(OsConstants.IPPROTO_TCP, loc, rem); 499 assertEquals(expectedUid, uid); 500 } 501 checkUdpEcho(String to, String expectedFrom)502 private void checkUdpEcho(String to, String expectedFrom) throws IOException { 503 DatagramSocket s; 504 InetAddress address = InetAddress.getByName(to); 505 if (address instanceof Inet6Address) { // http://b/18094870 506 s = new DatagramSocket(0, InetAddress.getByName("::")); 507 } else { 508 s = new DatagramSocket(); 509 } 510 s.setSoTimeout(SOCKET_TIMEOUT_MS); 511 512 Random random = new Random(); 513 byte[] data = new byte[random.nextInt(1650)]; 514 random.nextBytes(data); 515 DatagramPacket p = new DatagramPacket(data, data.length); 516 s.connect(address, 7); 517 518 if (expectedFrom != null) { 519 assertEquals("Unexpected source address: ", 520 expectedFrom, s.getLocalAddress().getHostAddress()); 521 } 522 523 try { 524 if (expectedFrom != null) { 525 s.send(p); 526 checkConnectionOwnerUidUdp(s, true); 527 s.receive(p); 528 MoreAsserts.assertEquals(data, p.getData()); 529 } else { 530 try { 531 s.send(p); 532 s.receive(p); 533 fail("Received unexpected reply"); 534 } catch (IOException expected) { 535 checkConnectionOwnerUidUdp(s, false); 536 } 537 } 538 } finally { 539 s.close(); 540 } 541 } 542 checkTrafficOnVpn()543 private void checkTrafficOnVpn() throws Exception { 544 checkUdpEcho("192.0.2.251", "192.0.2.2"); 545 checkUdpEcho("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe"); 546 checkPing("2001:db8:dead:beef::f00"); 547 checkTcpReflection("192.0.2.252", "192.0.2.2"); 548 checkTcpReflection("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe"); 549 } 550 checkNoTrafficOnVpn()551 private void checkNoTrafficOnVpn() throws Exception { 552 checkUdpEcho("192.0.2.251", null); 553 checkUdpEcho("2001:db8:dead:beef::f00", null); 554 checkTcpReflection("192.0.2.252", null); 555 checkTcpReflection("2001:db8:dead:beef::f00", null); 556 } 557 openSocketFd(String host, int port, int timeoutMs)558 private FileDescriptor openSocketFd(String host, int port, int timeoutMs) throws Exception { 559 Socket s = new Socket(host, port); 560 s.setSoTimeout(timeoutMs); 561 // Dup the filedescriptor so ParcelFileDescriptor's finalizer doesn't garbage collect it 562 // and cause our fd to become invalid. http://b/35927643 . 563 FileDescriptor fd = Os.dup(ParcelFileDescriptor.fromSocket(s).getFileDescriptor()); 564 s.close(); 565 return fd; 566 } 567 openSocketFdInOtherApp( String host, int port, int timeoutMs)568 private FileDescriptor openSocketFdInOtherApp( 569 String host, int port, int timeoutMs) throws Exception { 570 Log.d(TAG, String.format("Creating test socket in UID=%d, my UID=%d", 571 mRemoteSocketFactoryClient.getUid(), Os.getuid())); 572 FileDescriptor fd = mRemoteSocketFactoryClient.openSocketFd(host, port, TIMEOUT_MS); 573 return fd; 574 } 575 sendRequest(FileDescriptor fd, String host)576 private void sendRequest(FileDescriptor fd, String host) throws Exception { 577 String request = "GET /generate_204 HTTP/1.1\r\n" + 578 "Host: " + host + "\r\n" + 579 "Connection: keep-alive\r\n\r\n"; 580 byte[] requestBytes = request.getBytes(StandardCharsets.UTF_8); 581 int ret = Os.write(fd, requestBytes, 0, requestBytes.length); 582 Log.d(TAG, "Wrote " + ret + "bytes"); 583 584 String expected = "HTTP/1.1 204 No Content\r\n"; 585 byte[] response = new byte[expected.length()]; 586 Os.read(fd, response, 0, response.length); 587 588 String actual = new String(response, StandardCharsets.UTF_8); 589 assertEquals(expected, actual); 590 Log.d(TAG, "Got response: " + actual); 591 } 592 assertSocketStillOpen(FileDescriptor fd, String host)593 private void assertSocketStillOpen(FileDescriptor fd, String host) throws Exception { 594 try { 595 assertTrue(fd.valid()); 596 sendRequest(fd, host); 597 assertTrue(fd.valid()); 598 } finally { 599 Os.close(fd); 600 } 601 } 602 assertSocketClosed(FileDescriptor fd, String host)603 private void assertSocketClosed(FileDescriptor fd, String host) throws Exception { 604 try { 605 assertTrue(fd.valid()); 606 sendRequest(fd, host); 607 fail("Socket opened before VPN connects should be closed when VPN connects"); 608 } catch (ErrnoException expected) { 609 assertEquals(ECONNABORTED, expected.errno); 610 assertTrue(fd.valid()); 611 } finally { 612 Os.close(fd); 613 } 614 } 615 getContentResolver()616 private ContentResolver getContentResolver() { 617 return getInstrumentation().getContext().getContentResolver(); 618 } 619 isPrivateDnsInStrictMode()620 private boolean isPrivateDnsInStrictMode() { 621 return PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals( 622 Settings.Global.getString(getContentResolver(), PRIVATE_DNS_MODE_SETTING)); 623 } 624 storePrivateDnsSetting()625 private void storePrivateDnsSetting() { 626 mOldPrivateDnsMode = Settings.Global.getString(getContentResolver(), 627 PRIVATE_DNS_MODE_SETTING); 628 mOldPrivateDnsSpecifier = Settings.Global.getString(getContentResolver(), 629 PRIVATE_DNS_SPECIFIER_SETTING); 630 } 631 restorePrivateDnsSetting()632 private void restorePrivateDnsSetting() { 633 Settings.Global.putString(getContentResolver(), PRIVATE_DNS_MODE_SETTING, 634 mOldPrivateDnsMode); 635 Settings.Global.putString(getContentResolver(), PRIVATE_DNS_SPECIFIER_SETTING, 636 mOldPrivateDnsSpecifier); 637 } 638 639 // TODO: replace with CtsNetUtils.awaitPrivateDnsSetting in Q or above. expectPrivateDnsHostname(final String hostname)640 private void expectPrivateDnsHostname(final String hostname) throws Exception { 641 final NetworkRequest request = new NetworkRequest.Builder() 642 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) 643 .build(); 644 final CountDownLatch latch = new CountDownLatch(1); 645 final NetworkCallback callback = new NetworkCallback() { 646 @Override 647 public void onLinkPropertiesChanged(Network network, LinkProperties lp) { 648 if (network.equals(mNetwork) && 649 Objects.equals(lp.getPrivateDnsServerName(), hostname)) { 650 latch.countDown(); 651 } 652 } 653 }; 654 655 mCM.registerNetworkCallback(request, callback); 656 657 try { 658 assertTrue("Private DNS hostname was not " + hostname + " after " + TIMEOUT_MS + "ms", 659 latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 660 } finally { 661 mCM.unregisterNetworkCallback(callback); 662 } 663 } 664 setAndVerifyPrivateDns(boolean strictMode)665 private void setAndVerifyPrivateDns(boolean strictMode) throws Exception { 666 final ContentResolver cr = getInstrumentation().getContext().getContentResolver(); 667 String privateDnsHostname; 668 669 if (strictMode) { 670 privateDnsHostname = "vpncts-nx.metric.gstatic.com"; 671 Settings.Global.putString(cr, PRIVATE_DNS_SPECIFIER_SETTING, privateDnsHostname); 672 Settings.Global.putString(cr, PRIVATE_DNS_MODE_SETTING, 673 PRIVATE_DNS_MODE_PROVIDER_HOSTNAME); 674 } else { 675 Settings.Global.putString(cr, PRIVATE_DNS_MODE_SETTING, PRIVATE_DNS_MODE_OPPORTUNISTIC); 676 privateDnsHostname = null; 677 } 678 679 expectPrivateDnsHostname(privateDnsHostname); 680 681 String randomName = "vpncts-" + new Random().nextInt(1000000000) + "-ds.metric.gstatic.com"; 682 if (strictMode) { 683 // Strict mode private DNS is enabled. DNS lookups should fail, because the private DNS 684 // server name is invalid. 685 try { 686 InetAddress.getByName(randomName); 687 fail("VPN DNS lookup should fail with private DNS enabled"); 688 } catch (UnknownHostException expected) { 689 } 690 } else { 691 // Strict mode private DNS is disabled. DNS lookup should succeed, because the VPN 692 // provides no DNS servers, and thus DNS falls through to the default network. 693 assertNotNull("VPN DNS lookup should succeed with private DNS disabled", 694 InetAddress.getByName(randomName)); 695 } 696 } 697 698 // Tests that strict mode private DNS is used on VPNs. checkStrictModePrivateDns()699 private void checkStrictModePrivateDns() throws Exception { 700 final boolean initialMode = isPrivateDnsInStrictMode(); 701 setAndVerifyPrivateDns(!initialMode); 702 setAndVerifyPrivateDns(initialMode); 703 } 704 testDefault()705 public void testDefault() throws Exception { 706 if (!supportedHardware()) return; 707 // If adb TCP port opened, this test may running by adb over network. 708 // All of socket would be destroyed in this test. So this test don't 709 // support adb over network, see b/119382723. 710 if (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1 711 || SystemProperties.getInt("service.adb.tcp.port", -1) > -1) { 712 Log.i(TAG, "adb is running over the network, so skip this test"); 713 return; 714 } 715 716 final BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver( 717 getInstrumentation().getTargetContext(), MyVpnService.ACTION_ESTABLISHED); 718 receiver.register(); 719 720 // Test the behaviour of a variety of types of network callbacks. 721 final Network defaultNetwork = mCM.getActiveNetwork(); 722 final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback(); 723 final TestableNetworkCallback otherUidCallback = new TestableNetworkCallback(); 724 final TestableNetworkCallback myUidCallback = new TestableNetworkCallback(); 725 if (SdkLevel.isAtLeastS()) { 726 final int otherUid = 727 UserHandle.of(5 /* userId */).getUid(Process.FIRST_APPLICATION_UID); 728 final Handler h = new Handler(Looper.getMainLooper()); 729 runWithShellPermissionIdentity(() -> { 730 mCM.registerSystemDefaultNetworkCallback(systemDefaultCallback, h); 731 mCM.registerDefaultNetworkCallbackForUid(otherUid, otherUidCallback, h); 732 mCM.registerDefaultNetworkCallbackForUid(Process.myUid(), myUidCallback, h); 733 }, NETWORK_SETTINGS); 734 for (TestableNetworkCallback callback : 735 List.of(systemDefaultCallback, otherUidCallback, myUidCallback)) { 736 callback.expectAvailableCallbacks(defaultNetwork, false /* suspended */, 737 true /* validated */, false /* blocked */, TIMEOUT_MS); 738 } 739 } 740 741 FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS); 742 743 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 744 new String[] {"0.0.0.0/0", "::/0"}, 745 "", "", null, null /* underlyingNetworks */, false /* isAlwaysMetered */); 746 747 final Intent intent = receiver.awaitForBroadcast(TimeUnit.MINUTES.toMillis(1)); 748 assertNotNull("Failed to receive broadcast from VPN service", intent); 749 assertFalse("Wrong VpnService#isAlwaysOn", 750 intent.getBooleanExtra(MyVpnService.EXTRA_ALWAYS_ON, true)); 751 assertFalse("Wrong VpnService#isLockdownEnabled", 752 intent.getBooleanExtra(MyVpnService.EXTRA_LOCKDOWN_ENABLED, true)); 753 754 assertSocketClosed(fd, TEST_HOST); 755 756 checkTrafficOnVpn(); 757 758 final Network vpnNetwork = mCM.getActiveNetwork(); 759 myUidCallback.expectAvailableThenValidatedCallbacks(vpnNetwork, TIMEOUT_MS); 760 assertEquals(vpnNetwork, mCM.getActiveNetwork()); 761 assertNotEqual(defaultNetwork, vpnNetwork); 762 maybeExpectVpnTransportInfo(vpnNetwork); 763 assertEquals(TYPE_VPN, mCM.getNetworkInfo(vpnNetwork).getType()); 764 765 if (SdkLevel.isAtLeastS()) { 766 // Check that system default network callback has not seen any network changes, even 767 // though the app's default network changed. Also check that otherUidCallback saw no 768 // network changes, because otherUid is in a different user and not subject to the VPN. 769 // This needs to be done before testing private DNS because checkStrictModePrivateDns 770 // will set the private DNS server to a nonexistent name, which will cause validation to 771 // fail and could cause the default network to switch (e.g., from wifi to cellular). 772 systemDefaultCallback.assertNoCallback(); 773 otherUidCallback.assertNoCallback(); 774 mCM.unregisterNetworkCallback(systemDefaultCallback); 775 mCM.unregisterNetworkCallback(otherUidCallback); 776 mCM.unregisterNetworkCallback(myUidCallback); 777 } 778 779 checkStrictModePrivateDns(); 780 781 receiver.unregisterQuietly(); 782 } 783 testAppAllowed()784 public void testAppAllowed() throws Exception { 785 if (!supportedHardware()) return; 786 787 FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS); 788 789 // Shell app must not be put in here or it would kill the ADB-over-network use case 790 String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName; 791 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 792 new String[] {"192.0.2.0/24", "2001:db8::/32"}, 793 allowedApps, "", null, null /* underlyingNetworks */, false /* isAlwaysMetered */); 794 795 assertSocketClosed(fd, TEST_HOST); 796 797 checkTrafficOnVpn(); 798 799 maybeExpectVpnTransportInfo(mCM.getActiveNetwork()); 800 801 checkStrictModePrivateDns(); 802 } 803 testAppDisallowed()804 public void testAppDisallowed() throws Exception { 805 if (!supportedHardware()) return; 806 807 FileDescriptor localFd = openSocketFd(TEST_HOST, 80, TIMEOUT_MS); 808 FileDescriptor remoteFd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS); 809 810 String disallowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName; 811 // If adb TCP port opened, this test may running by adb over TCP. 812 // Add com.android.shell appllication into blacklist to exclude adb socket for VPN test, 813 // see b/119382723. 814 // Note: The test don't support running adb over network for root device 815 disallowedApps = disallowedApps + ",com.android.shell"; 816 Log.i(TAG, "Append shell app to disallowedApps: " + disallowedApps); 817 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 818 new String[] {"192.0.2.0/24", "2001:db8::/32"}, 819 "", disallowedApps, null, null /* underlyingNetworks */, 820 false /* isAlwaysMetered */); 821 822 assertSocketStillOpen(localFd, TEST_HOST); 823 assertSocketStillOpen(remoteFd, TEST_HOST); 824 825 checkNoTrafficOnVpn(); 826 827 final Network network = mCM.getActiveNetwork(); 828 final NetworkCapabilities nc = mCM.getNetworkCapabilities(network); 829 assertFalse(nc.hasTransport(TRANSPORT_VPN)); 830 } 831 testGetConnectionOwnerUidSecurity()832 public void testGetConnectionOwnerUidSecurity() throws Exception { 833 if (!supportedHardware()) return; 834 835 DatagramSocket s; 836 InetAddress address = InetAddress.getByName("localhost"); 837 s = new DatagramSocket(); 838 s.setSoTimeout(SOCKET_TIMEOUT_MS); 839 s.connect(address, 7); 840 InetSocketAddress loc = new InetSocketAddress(s.getLocalAddress(), s.getLocalPort()); 841 InetSocketAddress rem = new InetSocketAddress(s.getInetAddress(), s.getPort()); 842 try { 843 int uid = mCM.getConnectionOwnerUid(OsConstants.IPPROTO_TCP, loc, rem); 844 assertEquals("Only an active VPN app should see connection information", 845 INVALID_UID, uid); 846 } catch (SecurityException acceptable) { 847 // R and below throw SecurityException if a non-active VPN calls this method. 848 // As long as we can't actually get socket information, either behaviour is fine. 849 return; 850 } 851 } 852 testSetProxy()853 public void testSetProxy() throws Exception { 854 if (!supportedHardware()) return; 855 ProxyInfo initialProxy = mCM.getDefaultProxy(); 856 // Receiver for the proxy change broadcast. 857 BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver(); 858 proxyBroadcastReceiver.register(); 859 860 String allowedApps = mPackageName; 861 ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888); 862 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 863 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", 864 testProxyInfo, null /* underlyingNetworks */, false /* isAlwaysMetered */); 865 866 // Check that the proxy change broadcast is received 867 try { 868 assertNotNull("No proxy change was broadcast when VPN is connected.", 869 proxyBroadcastReceiver.awaitForBroadcast()); 870 } finally { 871 proxyBroadcastReceiver.unregisterQuietly(); 872 } 873 874 // Proxy is set correctly in network and in link properties. 875 assertNetworkHasExpectedProxy(testProxyInfo, mNetwork); 876 assertDefaultProxy(testProxyInfo); 877 878 proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver(); 879 proxyBroadcastReceiver.register(); 880 stopVpn(); 881 try { 882 assertNotNull("No proxy change was broadcast when VPN was disconnected.", 883 proxyBroadcastReceiver.awaitForBroadcast()); 884 } finally { 885 proxyBroadcastReceiver.unregisterQuietly(); 886 } 887 888 // After disconnecting from VPN, the proxy settings are the ones of the initial network. 889 assertDefaultProxy(initialProxy); 890 } 891 testSetProxyDisallowedApps()892 public void testSetProxyDisallowedApps() throws Exception { 893 if (!supportedHardware()) return; 894 ProxyInfo initialProxy = mCM.getDefaultProxy(); 895 896 // If adb TCP port opened, this test may running by adb over TCP. 897 // Add com.android.shell appllication into blacklist to exclude adb socket for VPN test, 898 // see b/119382723. 899 // Note: The test don't support running adb over network for root device 900 String disallowedApps = mPackageName + ",com.android.shell"; 901 ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888); 902 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 903 new String[] {"0.0.0.0/0", "::/0"}, "", disallowedApps, 904 testProxyInfo, null /* underlyingNetworks */, false /* isAlwaysMetered */); 905 906 // The disallowed app does has the proxy configs of the default network. 907 assertNetworkHasExpectedProxy(initialProxy, mCM.getActiveNetwork()); 908 assertDefaultProxy(initialProxy); 909 } 910 testNoProxy()911 public void testNoProxy() throws Exception { 912 if (!supportedHardware()) return; 913 ProxyInfo initialProxy = mCM.getDefaultProxy(); 914 BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver(); 915 proxyBroadcastReceiver.register(); 916 String allowedApps = mPackageName; 917 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 918 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null, 919 null /* underlyingNetworks */, false /* isAlwaysMetered */); 920 921 try { 922 assertNotNull("No proxy change was broadcast.", 923 proxyBroadcastReceiver.awaitForBroadcast()); 924 } finally { 925 proxyBroadcastReceiver.unregisterQuietly(); 926 } 927 928 // The VPN network has no proxy set. 929 assertNetworkHasExpectedProxy(null, mNetwork); 930 931 proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver(); 932 proxyBroadcastReceiver.register(); 933 stopVpn(); 934 try { 935 assertNotNull("No proxy change was broadcast.", 936 proxyBroadcastReceiver.awaitForBroadcast()); 937 } finally { 938 proxyBroadcastReceiver.unregisterQuietly(); 939 } 940 // After disconnecting from VPN, the proxy settings are the ones of the initial network. 941 assertDefaultProxy(initialProxy); 942 assertNetworkHasExpectedProxy(initialProxy, mCM.getActiveNetwork()); 943 } 944 testBindToNetworkWithProxy()945 public void testBindToNetworkWithProxy() throws Exception { 946 if (!supportedHardware()) return; 947 String allowedApps = mPackageName; 948 Network initialNetwork = mCM.getActiveNetwork(); 949 ProxyInfo initialProxy = mCM.getDefaultProxy(); 950 ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888); 951 // Receiver for the proxy change broadcast. 952 BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver(); 953 proxyBroadcastReceiver.register(); 954 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 955 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", 956 testProxyInfo, null /* underlyingNetworks */, false /* isAlwaysMetered */); 957 958 assertDefaultProxy(testProxyInfo); 959 mCM.bindProcessToNetwork(initialNetwork); 960 try { 961 assertNotNull("No proxy change was broadcast.", 962 proxyBroadcastReceiver.awaitForBroadcast()); 963 } finally { 964 proxyBroadcastReceiver.unregisterQuietly(); 965 } 966 assertDefaultProxy(initialProxy); 967 } 968 testVpnMeterednessWithNoUnderlyingNetwork()969 public void testVpnMeterednessWithNoUnderlyingNetwork() throws Exception { 970 if (!supportedHardware()) { 971 return; 972 } 973 // VPN is not routing any traffic i.e. its underlying networks is an empty array. 974 ArrayList<Network> underlyingNetworks = new ArrayList<>(); 975 String allowedApps = mPackageName; 976 977 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 978 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null, 979 underlyingNetworks, false /* isAlwaysMetered */); 980 981 // VPN should now be the active network. 982 assertEquals(mNetwork, mCM.getActiveNetwork()); 983 assertVpnTransportContains(NetworkCapabilities.TRANSPORT_VPN); 984 // VPN with no underlying networks should be metered by default. 985 assertTrue(isNetworkMetered(mNetwork)); 986 assertTrue(mCM.isActiveNetworkMetered()); 987 988 maybeExpectVpnTransportInfo(mCM.getActiveNetwork()); 989 } 990 testVpnMeterednessWithNullUnderlyingNetwork()991 public void testVpnMeterednessWithNullUnderlyingNetwork() throws Exception { 992 if (!supportedHardware()) { 993 return; 994 } 995 Network underlyingNetwork = mCM.getActiveNetwork(); 996 if (underlyingNetwork == null) { 997 Log.i(TAG, "testVpnMeterednessWithNullUnderlyingNetwork cannot execute" 998 + " unless there is an active network"); 999 return; 1000 } 1001 // VPN tracks platform default. 1002 ArrayList<Network> underlyingNetworks = null; 1003 String allowedApps = mPackageName; 1004 1005 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1006 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null, 1007 underlyingNetworks, false /*isAlwaysMetered */); 1008 1009 // Ensure VPN transports contains underlying network's transports. 1010 assertVpnTransportContains(underlyingNetwork); 1011 // Its meteredness should be same as that of underlying network. 1012 assertEquals(isNetworkMetered(underlyingNetwork), isNetworkMetered(mNetwork)); 1013 // Meteredness based on VPN capabilities and CM#isActiveNetworkMetered should be in sync. 1014 assertEquals(isNetworkMetered(mNetwork), mCM.isActiveNetworkMetered()); 1015 1016 maybeExpectVpnTransportInfo(mCM.getActiveNetwork()); 1017 } 1018 testVpnMeterednessWithNonNullUnderlyingNetwork()1019 public void testVpnMeterednessWithNonNullUnderlyingNetwork() throws Exception { 1020 if (!supportedHardware()) { 1021 return; 1022 } 1023 Network underlyingNetwork = mCM.getActiveNetwork(); 1024 if (underlyingNetwork == null) { 1025 Log.i(TAG, "testVpnMeterednessWithNonNullUnderlyingNetwork cannot execute" 1026 + " unless there is an active network"); 1027 return; 1028 } 1029 // VPN explicitly declares WiFi to be its underlying network. 1030 ArrayList<Network> underlyingNetworks = new ArrayList<>(1); 1031 underlyingNetworks.add(underlyingNetwork); 1032 String allowedApps = mPackageName; 1033 1034 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1035 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null, 1036 underlyingNetworks, false /* isAlwaysMetered */); 1037 1038 // Ensure VPN transports contains underlying network's transports. 1039 assertVpnTransportContains(underlyingNetwork); 1040 // Its meteredness should be same as that of underlying network. 1041 assertEquals(isNetworkMetered(underlyingNetwork), isNetworkMetered(mNetwork)); 1042 // Meteredness based on VPN capabilities and CM#isActiveNetworkMetered should be in sync. 1043 assertEquals(isNetworkMetered(mNetwork), mCM.isActiveNetworkMetered()); 1044 1045 maybeExpectVpnTransportInfo(mCM.getActiveNetwork()); 1046 } 1047 testAlwaysMeteredVpnWithNullUnderlyingNetwork()1048 public void testAlwaysMeteredVpnWithNullUnderlyingNetwork() throws Exception { 1049 if (!supportedHardware()) { 1050 return; 1051 } 1052 Network underlyingNetwork = mCM.getActiveNetwork(); 1053 if (underlyingNetwork == null) { 1054 Log.i(TAG, "testAlwaysMeteredVpnWithNullUnderlyingNetwork cannot execute" 1055 + " unless there is an active network"); 1056 return; 1057 } 1058 // VPN tracks platform default. 1059 ArrayList<Network> underlyingNetworks = null; 1060 String allowedApps = mPackageName; 1061 boolean isAlwaysMetered = true; 1062 1063 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1064 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null, 1065 underlyingNetworks, isAlwaysMetered); 1066 1067 // VPN's meteredness does not depend on underlying network since it is always metered. 1068 assertTrue(isNetworkMetered(mNetwork)); 1069 assertTrue(mCM.isActiveNetworkMetered()); 1070 1071 maybeExpectVpnTransportInfo(mCM.getActiveNetwork()); 1072 } 1073 testAlwaysMeteredVpnWithNonNullUnderlyingNetwork()1074 public void testAlwaysMeteredVpnWithNonNullUnderlyingNetwork() throws Exception { 1075 if (!supportedHardware()) { 1076 return; 1077 } 1078 Network underlyingNetwork = mCM.getActiveNetwork(); 1079 if (underlyingNetwork == null) { 1080 Log.i(TAG, "testAlwaysMeteredVpnWithNonNullUnderlyingNetwork cannot execute" 1081 + " unless there is an active network"); 1082 return; 1083 } 1084 // VPN explicitly declares its underlying network. 1085 ArrayList<Network> underlyingNetworks = new ArrayList<>(1); 1086 underlyingNetworks.add(underlyingNetwork); 1087 String allowedApps = mPackageName; 1088 boolean isAlwaysMetered = true; 1089 1090 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1091 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null, 1092 underlyingNetworks, isAlwaysMetered); 1093 1094 // VPN's meteredness does not depend on underlying network since it is always metered. 1095 assertTrue(isNetworkMetered(mNetwork)); 1096 assertTrue(mCM.isActiveNetworkMetered()); 1097 1098 maybeExpectVpnTransportInfo(mCM.getActiveNetwork()); 1099 } 1100 testB141603906()1101 public void testB141603906() throws Exception { 1102 if (!supportedHardware()) { 1103 return; 1104 } 1105 final InetSocketAddress src = new InetSocketAddress(0); 1106 final InetSocketAddress dst = new InetSocketAddress(0); 1107 final int NUM_THREADS = 8; 1108 final int NUM_SOCKETS = 5000; 1109 final Thread[] threads = new Thread[NUM_THREADS]; 1110 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1111 new String[] {"0.0.0.0/0", "::/0"}, 1112 "" /* allowedApplications */, "com.android.shell" /* disallowedApplications */, 1113 null /* proxyInfo */, null /* underlyingNetworks */, false /* isAlwaysMetered */); 1114 1115 for (int i = 0; i < NUM_THREADS; i++) { 1116 threads[i] = new Thread(() -> { 1117 for (int j = 0; j < NUM_SOCKETS; j++) { 1118 mCM.getConnectionOwnerUid(IPPROTO_TCP, src, dst); 1119 } 1120 }); 1121 } 1122 for (Thread thread : threads) { 1123 thread.start(); 1124 } 1125 for (Thread thread : threads) { 1126 thread.join(); 1127 } 1128 stopVpn(); 1129 } 1130 isNetworkMetered(Network network)1131 private boolean isNetworkMetered(Network network) { 1132 NetworkCapabilities nc = mCM.getNetworkCapabilities(network); 1133 return !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); 1134 } 1135 assertVpnTransportContains(Network underlyingNetwork)1136 private void assertVpnTransportContains(Network underlyingNetwork) { 1137 int[] transports = mCM.getNetworkCapabilities(underlyingNetwork).getTransportTypes(); 1138 assertVpnTransportContains(transports); 1139 } 1140 assertVpnTransportContains(int... transports)1141 private void assertVpnTransportContains(int... transports) { 1142 NetworkCapabilities vpnCaps = mCM.getNetworkCapabilities(mNetwork); 1143 for (int transport : transports) { 1144 assertTrue(vpnCaps.hasTransport(transport)); 1145 } 1146 } 1147 maybeExpectVpnTransportInfo(Network network)1148 private void maybeExpectVpnTransportInfo(Network network) { 1149 if (!SdkLevel.isAtLeastS()) return; 1150 final NetworkCapabilities vpnNc = mCM.getNetworkCapabilities(network); 1151 assertTrue(vpnNc.hasTransport(TRANSPORT_VPN)); 1152 final TransportInfo ti = vpnNc.getTransportInfo(); 1153 assertTrue(ti instanceof VpnTransportInfo); 1154 assertEquals(VpnManager.TYPE_VPN_SERVICE, ((VpnTransportInfo) ti).getType()); 1155 } 1156 assertDefaultProxy(ProxyInfo expected)1157 private void assertDefaultProxy(ProxyInfo expected) { 1158 assertEquals("Incorrect proxy config.", expected, mCM.getDefaultProxy()); 1159 String expectedHost = expected == null ? null : expected.getHost(); 1160 String expectedPort = expected == null ? null : String.valueOf(expected.getPort()); 1161 assertEquals("Incorrect proxy host system property.", expectedHost, 1162 System.getProperty("http.proxyHost")); 1163 assertEquals("Incorrect proxy port system property.", expectedPort, 1164 System.getProperty("http.proxyPort")); 1165 } 1166 assertNetworkHasExpectedProxy(ProxyInfo expected, Network network)1167 private void assertNetworkHasExpectedProxy(ProxyInfo expected, Network network) { 1168 LinkProperties lp = mCM.getLinkProperties(network); 1169 assertNotNull("The network link properties object is null.", lp); 1170 assertEquals("Incorrect proxy config.", expected, lp.getHttpProxy()); 1171 1172 assertEquals(expected, mCM.getProxyForNetwork(network)); 1173 } 1174 1175 class ProxyChangeBroadcastReceiver extends BlockingBroadcastReceiver { 1176 private boolean received; 1177 ProxyChangeBroadcastReceiver()1178 public ProxyChangeBroadcastReceiver() { 1179 super(VpnTest.this.getInstrumentation().getContext(), Proxy.PROXY_CHANGE_ACTION); 1180 received = false; 1181 } 1182 1183 @Override onReceive(Context context, Intent intent)1184 public void onReceive(Context context, Intent intent) { 1185 if (!received) { 1186 // Do not call onReceive() more than once. 1187 super.onReceive(context, intent); 1188 } 1189 received = true; 1190 } 1191 } 1192 1193 /** 1194 * Verifies that DownloadManager has CONNECTIVITY_USE_RESTRICTED_NETWORKS permission that can 1195 * bind socket to VPN when it is in VPN disallowed list but requested downloading app is in VPN 1196 * allowed list. 1197 * See b/165774987. 1198 */ testDownloadWithDownloadManagerDisallowed()1199 public void testDownloadWithDownloadManagerDisallowed() throws Exception { 1200 if (!supportedHardware()) return; 1201 1202 // Start a VPN with DownloadManager package in disallowed list. 1203 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1204 new String[] {"192.0.2.0/24", "2001:db8::/32"}, 1205 "" /* allowedApps */, "com.android.providers.downloads", null /* proxyInfo */, 1206 null /* underlyingNetworks */, false /* isAlwaysMetered */); 1207 1208 final Context context = VpnTest.this.getInstrumentation().getContext(); 1209 final DownloadManager dm = context.getSystemService(DownloadManager.class); 1210 final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver(); 1211 try { 1212 context.registerReceiver(receiver, 1213 new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); 1214 1215 // Enqueue a request and check only one download. 1216 final long id = dm.enqueue(new Request( 1217 Uri.parse("https://google-ipv6test.appspot.com/ip.js?fmt=text"))); 1218 assertEquals(1, getTotalNumberDownloads(dm, new Query())); 1219 assertEquals(1, getTotalNumberDownloads(dm, new Query().setFilterById(id))); 1220 1221 // Wait for download complete and check status. 1222 assertEquals(id, receiver.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 1223 assertEquals(1, getTotalNumberDownloads(dm, 1224 new Query().setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL))); 1225 1226 // Remove download. 1227 assertEquals(1, dm.remove(id)); 1228 assertEquals(0, getTotalNumberDownloads(dm, new Query())); 1229 } finally { 1230 context.unregisterReceiver(receiver); 1231 } 1232 } 1233 getTotalNumberDownloads(final DownloadManager dm, final Query query)1234 private static int getTotalNumberDownloads(final DownloadManager dm, final Query query) { 1235 try (Cursor cursor = dm.query(query)) { return cursor.getCount(); } 1236 } 1237 1238 private static class DownloadCompleteReceiver extends BroadcastReceiver { 1239 private final CompletableFuture<Long> future = new CompletableFuture<>(); 1240 1241 @Override onReceive(Context context, Intent intent)1242 public void onReceive(Context context, Intent intent) { 1243 future.complete(intent.getLongExtra( 1244 DownloadManager.EXTRA_DOWNLOAD_ID, -1 /* defaultValue */)); 1245 } 1246 get(long timeout, TimeUnit unit)1247 public long get(long timeout, TimeUnit unit) throws Exception { 1248 return future.get(timeout, unit); 1249 } 1250 } 1251 } 1252