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