• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.MANAGE_TEST_NETWORKS;
20 import static android.Manifest.permission.NETWORK_SETTINGS;
21 import static android.Manifest.permission.READ_DEVICE_CONFIG;
22 import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
23 import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
24 import static android.content.pm.PackageManager.FEATURE_WIFI;
25 import static android.net.ConnectivityManager.TYPE_VPN;
26 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
27 import static android.os.Process.INVALID_UID;
28 import static android.system.OsConstants.AF_INET;
29 import static android.system.OsConstants.AF_INET6;
30 import static android.system.OsConstants.ECONNABORTED;
31 import static android.system.OsConstants.IPPROTO_ICMP;
32 import static android.system.OsConstants.IPPROTO_ICMPV6;
33 import static android.system.OsConstants.IPPROTO_TCP;
34 import static android.system.OsConstants.POLLIN;
35 import static android.system.OsConstants.SOCK_DGRAM;
36 import static android.test.MoreAsserts.assertNotEqual;
37 
38 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
39 
40 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
41 import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_DATA_RECEIVED;
42 import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_ERROR;
43 import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_PAUSED;
44 import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_RESUMED;
45 import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_STARTED;
46 import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_STOPPED;
47 import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_LOCKDOWN_VPN;
48 import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_NONE;
49 import static com.android.networkstack.apishim.ConstantsShim.RECEIVER_EXPORTED;
50 import static com.android.testutils.Cleanup.testAndCleanup;
51 import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
52 import static com.android.testutils.RecorderCallback.CallbackEntry.BLOCKED_STATUS_INT;
53 
54 import static org.junit.Assert.assertEquals;
55 import static org.junit.Assert.assertFalse;
56 import static org.junit.Assert.assertNotNull;
57 import static org.junit.Assert.assertTrue;
58 import static org.junit.Assert.fail;
59 import static org.junit.Assume.assumeTrue;
60 
61 import android.annotation.Nullable;
62 import android.app.Activity;
63 import android.app.DownloadManager;
64 import android.app.DownloadManager.Query;
65 import android.app.DownloadManager.Request;
66 import android.content.BroadcastReceiver;
67 import android.content.ContentResolver;
68 import android.content.Context;
69 import android.content.Intent;
70 import android.content.IntentFilter;
71 import android.content.pm.PackageManager;
72 import android.database.Cursor;
73 import android.net.ConnectivityManager;
74 import android.net.ConnectivityManager.NetworkCallback;
75 import android.net.IpSecManager;
76 import android.net.LinkAddress;
77 import android.net.LinkProperties;
78 import android.net.Network;
79 import android.net.NetworkCapabilities;
80 import android.net.NetworkRequest;
81 import android.net.Proxy;
82 import android.net.ProxyInfo;
83 import android.net.SocketKeepalive;
84 import android.net.TestNetworkInterface;
85 import android.net.TestNetworkManager;
86 import android.net.TransportInfo;
87 import android.net.Uri;
88 import android.net.VpnManager;
89 import android.net.VpnService;
90 import android.net.VpnTransportInfo;
91 import android.net.cts.util.CtsNetUtils;
92 import android.net.util.KeepaliveUtils;
93 import android.net.wifi.WifiManager;
94 import android.os.Build;
95 import android.os.Handler;
96 import android.os.Looper;
97 import android.os.ParcelFileDescriptor;
98 import android.os.Process;
99 import android.os.SystemProperties;
100 import android.os.UserHandle;
101 import android.provider.DeviceConfig;
102 import android.provider.Settings;
103 import android.support.test.uiautomator.UiDevice;
104 import android.support.test.uiautomator.UiObject;
105 import android.support.test.uiautomator.UiSelector;
106 import android.system.ErrnoException;
107 import android.system.Os;
108 import android.system.OsConstants;
109 import android.system.StructPollfd;
110 import android.test.MoreAsserts;
111 import android.text.TextUtils;
112 import android.util.ArraySet;
113 import android.util.Log;
114 import android.util.Range;
115 
116 import androidx.test.ext.junit.runners.AndroidJUnit4;
117 
118 import com.android.compatibility.common.util.BlockingBroadcastReceiver;
119 import com.android.modules.utils.build.SdkLevel;
120 import com.android.net.module.util.ArrayTrackRecord;
121 import com.android.net.module.util.CollectionUtils;
122 import com.android.net.module.util.PacketBuilder;
123 import com.android.testutils.DevSdkIgnoreRule;
124 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
125 import com.android.testutils.RecorderCallback;
126 import com.android.testutils.RecorderCallback.CallbackEntry;
127 import com.android.testutils.TestableNetworkCallback;
128 
129 import org.junit.After;
130 import org.junit.Before;
131 import org.junit.Rule;
132 import org.junit.Test;
133 import org.junit.runner.RunWith;
134 
135 import java.io.Closeable;
136 import java.io.FileDescriptor;
137 import java.io.IOException;
138 import java.io.InputStream;
139 import java.io.OutputStream;
140 import java.net.DatagramPacket;
141 import java.net.DatagramSocket;
142 import java.net.Inet4Address;
143 import java.net.Inet6Address;
144 import java.net.InetAddress;
145 import java.net.InetSocketAddress;
146 import java.net.ServerSocket;
147 import java.net.Socket;
148 import java.net.UnknownHostException;
149 import java.nio.ByteBuffer;
150 import java.nio.charset.StandardCharsets;
151 import java.util.ArrayList;
152 import java.util.List;
153 import java.util.Objects;
154 import java.util.Random;
155 import java.util.UUID;
156 import java.util.concurrent.CompletableFuture;
157 import java.util.concurrent.CountDownLatch;
158 import java.util.concurrent.Executor;
159 import java.util.concurrent.TimeUnit;
160 
161 /**
162  * Tests for the VpnService API.
163  *
164  * These tests establish a VPN via the VpnService API, and have the service reflect the packets back
165  * to the device without causing any network traffic. This allows testing the local VPN data path
166  * without a network connection or a VPN server.
167  *
168  * Note: in Lollipop, VPN functionality relies on kernel support for UID-based routing. If these
169  * tests fail, it may be due to the lack of kernel support. The necessary patches can be
170  * cherry-picked from the Android common kernel trees:
171  *
172  * android-3.10:
173  *   https://android-review.googlesource.com/#/c/99220/
174  *   https://android-review.googlesource.com/#/c/100545/
175  *
176  * android-3.4:
177  *   https://android-review.googlesource.com/#/c/99225/
178  *   https://android-review.googlesource.com/#/c/100557/
179  *
180  * To ensure that the kernel has the required commits, run the kernel unit
181  * tests described at:
182  *
183  *   https://source.android.com/devices/tech/config/kernel_network_tests.html
184  *
185  */
186 @RunWith(AndroidJUnit4.class)
187 public class VpnTest {
188 
189     // These are neither public nor @TestApi.
190     // TODO: add them to @TestApi.
191     private static final String PRIVATE_DNS_MODE_SETTING = "private_dns_mode";
192     private static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
193     private static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
194     private static final String PRIVATE_DNS_SPECIFIER_SETTING = "private_dns_specifier";
195     private static final int NETWORK_CALLBACK_TIMEOUT_MS = 30_000;
196 
197     private static final LinkAddress TEST_IP4_DST_ADDR = new LinkAddress("198.51.100.1/24");
198     private static final LinkAddress TEST_IP4_SRC_ADDR = new LinkAddress("198.51.100.2/24");
199     private static final LinkAddress TEST_IP6_DST_ADDR = new LinkAddress("2001:db8:1:3::1/64");
200     private static final LinkAddress TEST_IP6_SRC_ADDR = new LinkAddress("2001:db8:1:3::2/64");
201     private static final short TEST_SRC_PORT = 5555;
202 
203     public static String TAG = "VpnTest";
204     public static int TIMEOUT_MS = 3 * 1000;
205     public static int SOCKET_TIMEOUT_MS = 100;
206     public static String TEST_HOST = "connectivitycheck.gstatic.com";
207 
208     private static final String AUTOMATIC_ON_OFF_KEEPALIVE_VERSION =
209                 "automatic_on_off_keepalive_version";
210     // Enabled since version 1 means it's always enabled because the version is always above 1
211     private static final String AUTOMATIC_ON_OFF_KEEPALIVE_ENABLED = "1";
212     private static final long TEST_TCP_POLLING_TIMER_EXPIRED_PERIOD_MS = 60_000L;
213 
214     private UiDevice mDevice;
215     private MyActivity mActivity;
216     private String mPackageName;
217     private ConnectivityManager mCM;
218     private WifiManager mWifiManager;
219     private RemoteSocketFactoryClient mRemoteSocketFactoryClient;
220     private CtsNetUtils mCtsNetUtils;
221     private PackageManager mPackageManager;
222     private Context mTestContext;
223     private Context mTargetContext;
224     Network mNetwork;
225     final Object mLock = new Object();
226     final Object mLockShutdown = new Object();
227 
228     private String mOldPrivateDnsMode;
229     private String mOldPrivateDnsSpecifier;
230 
231     // The registered callbacks.
232     private List<NetworkCallback> mRegisteredCallbacks = new ArrayList<>();
233 
234     @Rule
235     public final DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
236 
supportedHardware()237     private boolean supportedHardware() {
238         final PackageManager pm = getInstrumentation().getContext().getPackageManager();
239         return !pm.hasSystemFeature("android.hardware.type.watch");
240     }
241 
launchActivity(String packageName, Class<T> activityClass)242     public final <T extends Activity> T launchActivity(String packageName, Class<T> activityClass) {
243         final Intent intent = new Intent(Intent.ACTION_MAIN);
244         intent.setClassName(packageName, activityClass.getName());
245         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
246         final T activity = (T) getInstrumentation().startActivitySync(intent);
247         getInstrumentation().waitForIdleSync();
248         return activity;
249     }
250 
251     @Before
setUp()252     public void setUp() throws Exception {
253         mNetwork = null;
254         mTestContext = getInstrumentation().getContext();
255         mTargetContext = getInstrumentation().getTargetContext();
256         storePrivateDnsSetting();
257         mDevice = UiDevice.getInstance(getInstrumentation());
258         mActivity = launchActivity(mTargetContext.getPackageName(), MyActivity.class);
259         mPackageName = mActivity.getPackageName();
260         mCM = (ConnectivityManager) mActivity.getSystemService(Context.CONNECTIVITY_SERVICE);
261         mWifiManager = (WifiManager) mActivity.getSystemService(Context.WIFI_SERVICE);
262         mRemoteSocketFactoryClient = new RemoteSocketFactoryClient(mActivity);
263         mRemoteSocketFactoryClient.bind();
264         mDevice.waitForIdle();
265         mCtsNetUtils = new CtsNetUtils(mTestContext);
266         mPackageManager = mTestContext.getPackageManager();
267     }
268 
269     @After
tearDown()270     public void tearDown() throws Exception {
271         restorePrivateDnsSetting();
272         mRemoteSocketFactoryClient.unbind();
273         mCtsNetUtils.tearDown();
274         Log.i(TAG, "Stopping VPN");
275         stopVpn();
276         unregisterRegisteredCallbacks();
277         mActivity.finish();
278     }
279 
registerNetworkCallback(NetworkRequest request, NetworkCallback callback)280     private void registerNetworkCallback(NetworkRequest request, NetworkCallback callback) {
281         mCM.registerNetworkCallback(request, callback);
282         mRegisteredCallbacks.add(callback);
283     }
284 
registerDefaultNetworkCallback(NetworkCallback callback)285     private void registerDefaultNetworkCallback(NetworkCallback callback) {
286         mCM.registerDefaultNetworkCallback(callback);
287         mRegisteredCallbacks.add(callback);
288     }
289 
registerSystemDefaultNetworkCallback(NetworkCallback callback, Handler h)290     private void registerSystemDefaultNetworkCallback(NetworkCallback callback, Handler h) {
291         mCM.registerSystemDefaultNetworkCallback(callback, h);
292         mRegisteredCallbacks.add(callback);
293     }
294 
registerDefaultNetworkCallbackForUid(int uid, NetworkCallback callback, Handler h)295     private void registerDefaultNetworkCallbackForUid(int uid, NetworkCallback callback,
296             Handler h) {
297         mCM.registerDefaultNetworkCallbackForUid(uid, callback, h);
298         mRegisteredCallbacks.add(callback);
299     }
300 
unregisterRegisteredCallbacks()301     private void unregisterRegisteredCallbacks() {
302         for (NetworkCallback callback: mRegisteredCallbacks) {
303             mCM.unregisterNetworkCallback(callback);
304         }
305     }
306 
prepareVpn()307     private void prepareVpn() throws Exception {
308         final int REQUEST_ID = 42;
309 
310         // Attempt to prepare.
311         Log.i(TAG, "Preparing VPN");
312         Intent intent = VpnService.prepare(mActivity);
313 
314         if (intent != null) {
315             // Start the confirmation dialog and click OK.
316             mActivity.startActivityForResult(intent, REQUEST_ID);
317             mDevice.waitForIdle();
318 
319             String packageName = intent.getComponent().getPackageName();
320             String resourceIdRegex = "android:id/button1$|button_start_vpn";
321             final UiObject okButton = new UiObject(new UiSelector()
322                     .className("android.widget.Button")
323                     .packageName(packageName)
324                     .resourceIdMatches(resourceIdRegex));
325             if (okButton.waitForExists(TIMEOUT_MS) == false) {
326                 mActivity.finishActivity(REQUEST_ID);
327                 fail("VpnService.prepare returned an Intent for '" + intent.getComponent() + "' " +
328                      "to display the VPN confirmation dialog, but this test could not find the " +
329                      "button to allow the VPN application to connect. Please ensure that the "  +
330                      "component displays a button with a resource ID matching the regexp: '" +
331                      resourceIdRegex + "'.");
332             }
333 
334             // Click the button and wait for RESULT_OK.
335             okButton.click();
336             try {
337                 int result = mActivity.getResult(TIMEOUT_MS);
338                 if (result != MyActivity.RESULT_OK) {
339                     fail("The VPN confirmation dialog did not return RESULT_OK when clicking on " +
340                          "the button matching the regular expression '" + resourceIdRegex +
341                          "' of " + intent.getComponent() + "'. Please ensure that clicking on " +
342                          "that button allows the VPN application to connect. " +
343                          "Return value: " + result);
344                 }
345             } catch (InterruptedException e) {
346                 fail("VPN confirmation dialog did not return after " + TIMEOUT_MS + "ms");
347             }
348 
349             // Now we should be prepared.
350             intent = VpnService.prepare(mActivity);
351             if (intent != null) {
352                 fail("VpnService.prepare returned non-null even after the VPN dialog " +
353                      intent.getComponent() + "returned RESULT_OK.");
354             }
355         }
356     }
357 
updateUnderlyingNetworks(@ullable ArrayList<Network> underlyingNetworks)358     private void updateUnderlyingNetworks(@Nullable ArrayList<Network> underlyingNetworks)
359             throws Exception {
360         final Intent intent = new Intent(mActivity, MyVpnService.class)
361                 .putExtra(mPackageName + ".cmd", MyVpnService.CMD_UPDATE_UNDERLYING_NETWORKS)
362                 .putParcelableArrayListExtra(
363                         mPackageName + ".underlyingNetworks", underlyingNetworks);
364         mActivity.startService(intent);
365     }
366 
establishVpn(String[] addresses, String[] routes, String[] excludedRoutes, String allowedApplications, String disallowedApplications, @Nullable ProxyInfo proxyInfo, @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered, boolean addRoutesByIpPrefix)367     private void establishVpn(String[] addresses, String[] routes, String[] excludedRoutes,
368             String allowedApplications, String disallowedApplications,
369             @Nullable ProxyInfo proxyInfo, @Nullable ArrayList<Network> underlyingNetworks,
370             boolean isAlwaysMetered, boolean addRoutesByIpPrefix)
371             throws Exception {
372         final Intent intent = new Intent(mActivity, MyVpnService.class)
373                 .putExtra(mPackageName + ".cmd", MyVpnService.CMD_CONNECT)
374                 .putExtra(mPackageName + ".addresses", TextUtils.join(",", addresses))
375                 .putExtra(mPackageName + ".routes", TextUtils.join(",", routes))
376                 .putExtra(mPackageName + ".excludedRoutes", TextUtils.join(",", excludedRoutes))
377                 .putExtra(mPackageName + ".allowedapplications", allowedApplications)
378                 .putExtra(mPackageName + ".disallowedapplications", disallowedApplications)
379                 .putExtra(mPackageName + ".httpProxy", proxyInfo)
380                 .putParcelableArrayListExtra(
381                         mPackageName + ".underlyingNetworks", underlyingNetworks)
382                 .putExtra(mPackageName + ".isAlwaysMetered", isAlwaysMetered)
383                 .putExtra(mPackageName + ".addRoutesByIpPrefix", addRoutesByIpPrefix);
384         mActivity.startService(intent);
385     }
386 
387     // 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)388     private void startVpn(
389             String[] addresses, String[] routes, String allowedApplications,
390             String disallowedApplications, @Nullable ProxyInfo proxyInfo,
391             @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered)
392             throws Exception {
393         startVpn(addresses, routes, new String[0] /* excludedRoutes */, allowedApplications,
394                 disallowedApplications, proxyInfo, underlyingNetworks, isAlwaysMetered);
395     }
396 
startVpn( String[] addresses, String[] routes, String[] excludedRoutes, String allowedApplications, String disallowedApplications, @Nullable ProxyInfo proxyInfo, @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered)397     private void startVpn(
398             String[] addresses, String[] routes, String[] excludedRoutes,
399             String allowedApplications, String disallowedApplications,
400             @Nullable ProxyInfo proxyInfo,
401             @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered)
402             throws Exception {
403         startVpn(addresses, routes, excludedRoutes, allowedApplications, disallowedApplications,
404                 proxyInfo, underlyingNetworks, isAlwaysMetered, false /* addRoutesByIpPrefix */);
405     }
406 
startVpn( String[] addresses, String[] routes, String[] excludedRoutes, String allowedApplications, String disallowedApplications, @Nullable ProxyInfo proxyInfo, @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered, boolean addRoutesByIpPrefix)407     private void startVpn(
408             String[] addresses, String[] routes, String[] excludedRoutes,
409             String allowedApplications, String disallowedApplications,
410             @Nullable ProxyInfo proxyInfo,
411             @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered,
412             boolean addRoutesByIpPrefix)
413             throws Exception {
414         prepareVpn();
415 
416         // Register a callback so we will be notified when our VPN comes up.
417         final NetworkRequest request = new NetworkRequest.Builder()
418                 .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
419                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
420                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
421                 .build();
422         final NetworkCallback callback = new NetworkCallback() {
423             public void onAvailable(Network network) {
424                 synchronized (mLock) {
425                     Log.i(TAG, "Got available callback for network=" + network);
426                     mNetwork = network;
427                     mLock.notify();
428                 }
429             }
430         };
431         registerNetworkCallback(request, callback);
432 
433         // Start the service and wait up for TIMEOUT_MS ms for the VPN to come up.
434         establishVpn(addresses, routes, excludedRoutes, allowedApplications, disallowedApplications,
435                 proxyInfo, underlyingNetworks, isAlwaysMetered, addRoutesByIpPrefix);
436         synchronized (mLock) {
437             if (mNetwork == null) {
438                  Log.i(TAG, "bf mLock");
439                  mLock.wait(TIMEOUT_MS);
440                  Log.i(TAG, "af mLock");
441             }
442         }
443 
444         if (mNetwork == null) {
445             fail("VPN did not become available after " + TIMEOUT_MS + "ms");
446         }
447     }
448 
stopVpn()449     private void stopVpn() {
450         // Register a callback so we will be notified when our VPN comes up.
451         final NetworkRequest request = new NetworkRequest.Builder()
452                 .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
453                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
454                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
455                 .build();
456         final NetworkCallback callback = new NetworkCallback() {
457             public void onLost(Network network) {
458                 synchronized (mLockShutdown) {
459                     Log.i(TAG, "Got lost callback for network=" + network
460                             + ",mNetwork = " + mNetwork);
461                     if( mNetwork == network){
462                         mLockShutdown.notify();
463                     }
464                 }
465             }
466        };
467         registerNetworkCallback(request, callback);
468         // Simply calling mActivity.stopService() won't stop the service, because the system binds
469         // to the service for the purpose of sending it a revoke command if another VPN comes up,
470         // and stopping a bound service has no effect. Instead, "start" the service again with an
471         // Intent that tells it to disconnect.
472         Intent intent = new Intent(mActivity, MyVpnService.class)
473                 .putExtra(mPackageName + ".cmd", MyVpnService.CMD_DISCONNECT);
474         mActivity.startService(intent);
475         synchronized (mLockShutdown) {
476             try {
477                  Log.i(TAG, "bf mLockShutdown");
478                  mLockShutdown.wait(TIMEOUT_MS);
479                  Log.i(TAG, "af mLockShutdown");
480             } catch(InterruptedException e) {}
481         }
482     }
483 
closeQuietly(Closeable c)484     private static void closeQuietly(Closeable c) {
485         if (c != null) {
486             try {
487                 c.close();
488             } catch (IOException e) {
489             }
490         }
491     }
492 
checkPing(String to)493     private static void checkPing(String to) throws IOException, ErrnoException {
494         InetAddress address = InetAddress.getByName(to);
495         FileDescriptor s;
496         final int LENGTH = 64;
497         byte[] packet = new byte[LENGTH];
498         byte[] header;
499 
500         // Construct a ping packet.
501         Random random = new Random();
502         random.nextBytes(packet);
503         if (address instanceof Inet6Address) {
504             s = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
505             header = new byte[] { (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
506         } else {
507             // Note that this doesn't actually work due to http://b/18558481 .
508             s = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
509             header = new byte[] { (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
510         }
511         System.arraycopy(header, 0, packet, 0, header.length);
512 
513         // Send the packet.
514         int port = random.nextInt(65534) + 1;
515         Os.connect(s, address, port);
516         Os.write(s, packet, 0, packet.length);
517 
518         // Expect a reply.
519         StructPollfd pollfd = new StructPollfd();
520         pollfd.events = (short) POLLIN;  // "error: possible loss of precision"
521         pollfd.fd = s;
522         int ret = Os.poll(new StructPollfd[] { pollfd }, SOCKET_TIMEOUT_MS);
523         assertEquals("Expected reply after sending ping", 1, ret);
524 
525         byte[] reply = new byte[LENGTH];
526         int read = Os.read(s, reply, 0, LENGTH);
527         assertEquals(LENGTH, read);
528 
529         // Find out what the kernel set the ICMP ID to.
530         InetSocketAddress local = (InetSocketAddress) Os.getsockname(s);
531         port = local.getPort();
532         packet[4] = (byte) ((port >> 8) & 0xff);
533         packet[5] = (byte) (port & 0xff);
534 
535         // Check the contents.
536         if (packet[0] == (byte) 0x80) {
537             packet[0] = (byte) 0x81;
538         } else {
539             packet[0] = 0;
540         }
541         // Zero out the checksum in the reply so it matches the uninitialized checksum in packet.
542         reply[2] = reply[3] = 0;
543         MoreAsserts.assertEquals(packet, reply);
544     }
545 
546     // Writes data to out and checks that it appears identically on in.
writeAndCheckData( OutputStream out, InputStream in, byte[] data)547     private static void writeAndCheckData(
548             OutputStream out, InputStream in, byte[] data) throws IOException {
549         out.write(data, 0, data.length);
550         out.flush();
551 
552         byte[] read = new byte[data.length];
553         int bytesRead = 0, totalRead = 0;
554         do {
555             bytesRead = in.read(read, totalRead, read.length - totalRead);
556             totalRead += bytesRead;
557         } while (bytesRead >= 0 && totalRead < data.length);
558         assertEquals(totalRead, data.length);
559         MoreAsserts.assertEquals(data, read);
560     }
561 
checkTcpReflection(String to, String expectedFrom)562     private void checkTcpReflection(String to, String expectedFrom) throws IOException {
563         // Exercise TCP over the VPN by "connecting to ourselves". We open a server socket and a
564         // client socket, and connect the client socket to a remote host, with the port of the
565         // server socket. The PacketReflector reflects the packets, changing the source addresses
566         // but not the ports, so our client socket is connected to our server socket, though both
567         // sockets think their peers are on the "remote" IP address.
568 
569         // Open a listening socket.
570         ServerSocket listen = new ServerSocket(0, 10, InetAddress.getByName("::"));
571 
572         // Connect the client socket to it.
573         InetAddress toAddr = InetAddress.getByName(to);
574         Socket client = new Socket();
575         try {
576             client.connect(new InetSocketAddress(toAddr, listen.getLocalPort()), SOCKET_TIMEOUT_MS);
577             if (expectedFrom == null) {
578                 closeQuietly(listen);
579                 closeQuietly(client);
580                 fail("Expected connection to fail, but it succeeded.");
581             }
582         } catch (IOException e) {
583             if (expectedFrom != null) {
584                 closeQuietly(listen);
585                 fail("Expected connection to succeed, but it failed.");
586             } else {
587                 // We expected the connection to fail, and it did, so there's nothing more to test.
588                 return;
589             }
590         }
591 
592         // The connection succeeded, and we expected it to succeed. Send some data; if things are
593         // working, the data will be sent to the VPN, reflected by the PacketReflector, and arrive
594         // at our server socket. For good measure, send some data in the other direction.
595         Socket server = null;
596         try {
597             // Accept the connection on the server side.
598             listen.setSoTimeout(SOCKET_TIMEOUT_MS);
599             server = listen.accept();
600             checkConnectionOwnerUidTcp(client);
601             checkConnectionOwnerUidTcp(server);
602             // Check that the source and peer addresses are as expected.
603             assertEquals(expectedFrom, client.getLocalAddress().getHostAddress());
604             assertEquals(expectedFrom, server.getLocalAddress().getHostAddress());
605             assertEquals(
606                     new InetSocketAddress(toAddr, client.getLocalPort()),
607                     server.getRemoteSocketAddress());
608             assertEquals(
609                     new InetSocketAddress(toAddr, server.getLocalPort()),
610                     client.getRemoteSocketAddress());
611 
612             // Now write some data.
613             final int LENGTH = 32768;
614             byte[] data = new byte[LENGTH];
615             new Random().nextBytes(data);
616 
617             // Make sure our writes don't block or time out, because we're single-threaded and can't
618             // read and write at the same time.
619             server.setReceiveBufferSize(LENGTH * 2);
620             client.setSendBufferSize(LENGTH * 2);
621             client.setSoTimeout(SOCKET_TIMEOUT_MS);
622             server.setSoTimeout(SOCKET_TIMEOUT_MS);
623 
624             // Send some data from client to server, then from server to client.
625             writeAndCheckData(client.getOutputStream(), server.getInputStream(), data);
626             writeAndCheckData(server.getOutputStream(), client.getInputStream(), data);
627         } finally {
628             closeQuietly(listen);
629             closeQuietly(client);
630             closeQuietly(server);
631         }
632     }
633 
checkConnectionOwnerUidUdp(DatagramSocket s, boolean expectSuccess)634     private void checkConnectionOwnerUidUdp(DatagramSocket s, boolean expectSuccess) {
635         final int expectedUid = expectSuccess ? Process.myUid() : INVALID_UID;
636         InetSocketAddress loc = new InetSocketAddress(s.getLocalAddress(), s.getLocalPort());
637         InetSocketAddress rem = new InetSocketAddress(s.getInetAddress(), s.getPort());
638         int uid = mCM.getConnectionOwnerUid(OsConstants.IPPROTO_UDP, loc, rem);
639         assertEquals(expectedUid, uid);
640     }
641 
checkConnectionOwnerUidTcp(Socket s)642     private void checkConnectionOwnerUidTcp(Socket s) {
643         final int expectedUid = Process.myUid();
644         InetSocketAddress loc = new InetSocketAddress(s.getLocalAddress(), s.getLocalPort());
645         InetSocketAddress rem = new InetSocketAddress(s.getInetAddress(), s.getPort());
646         int uid = mCM.getConnectionOwnerUid(OsConstants.IPPROTO_TCP, loc, rem);
647         assertEquals(expectedUid, uid);
648     }
649 
checkUdpEcho(String to, String expectedFrom)650     private void checkUdpEcho(String to, String expectedFrom) throws IOException {
651         checkUdpEcho(to, expectedFrom, expectedFrom != null);
652     }
653 
checkUdpEcho(String to, String expectedFrom, boolean expectConnectionOwnerIsVisible)654     private void checkUdpEcho(String to, String expectedFrom,
655             boolean expectConnectionOwnerIsVisible)
656             throws IOException {
657         DatagramSocket s;
658         InetAddress address = InetAddress.getByName(to);
659         if (address instanceof Inet6Address) {  // http://b/18094870
660             s = new DatagramSocket(0, InetAddress.getByName("::"));
661         } else {
662             s = new DatagramSocket();
663         }
664         s.setSoTimeout(SOCKET_TIMEOUT_MS);
665 
666         Random random = new Random();
667         byte[] data = new byte[random.nextInt(1650)];
668         random.nextBytes(data);
669         DatagramPacket p = new DatagramPacket(data, data.length);
670         s.connect(address, 7);
671 
672         if (expectedFrom != null) {
673             assertEquals("Unexpected source address: ",
674                          expectedFrom, s.getLocalAddress().getHostAddress());
675         }
676 
677         try {
678             if (expectedFrom != null) {
679                 s.send(p);
680                 checkConnectionOwnerUidUdp(s, expectConnectionOwnerIsVisible);
681                 s.receive(p);
682                 MoreAsserts.assertEquals(data, p.getData());
683             } else {
684                 try {
685                     s.send(p);
686                     s.receive(p);
687                     fail("Received unexpected reply");
688                 } catch (IOException expected) {
689                     checkConnectionOwnerUidUdp(s, expectConnectionOwnerIsVisible);
690                 }
691             }
692         } finally {
693             s.close();
694         }
695     }
696 
checkTrafficOnVpn(String destination)697     private void checkTrafficOnVpn(String destination) throws Exception {
698         final InetAddress address = InetAddress.getByName(destination);
699 
700         if (address instanceof Inet6Address) {
701             checkUdpEcho(destination, "2001:db8:1:2::ffe");
702             checkPing(destination);
703             checkTcpReflection(destination, "2001:db8:1:2::ffe");
704         } else {
705             checkUdpEcho(destination, "192.0.2.2");
706             checkTcpReflection(destination, "192.0.2.2");
707         }
708 
709     }
710 
checkNoTrafficOnVpn(String destination)711     private void checkNoTrafficOnVpn(String destination) throws IOException {
712         checkUdpEcho(destination, null);
713         checkTcpReflection(destination, null);
714     }
715 
checkTrafficOnVpn()716     private void checkTrafficOnVpn() throws Exception {
717         checkTrafficOnVpn("192.0.2.251");
718         checkTrafficOnVpn("2001:db8:dead:beef::f00");
719     }
720 
checkNoTrafficOnVpn()721     private void checkNoTrafficOnVpn() throws Exception {
722         checkNoTrafficOnVpn("192.0.2.251");
723         checkNoTrafficOnVpn("2001:db8:dead:beef::f00");
724     }
725 
checkTrafficBypassesVpn(String destination)726     private void checkTrafficBypassesVpn(String destination) throws Exception {
727         checkUdpEcho(destination, null, true /* expectVpnOwnedConnection */);
728         checkTcpReflection(destination, null);
729     }
730 
openSocketFd(String host, int port, int timeoutMs)731     private FileDescriptor openSocketFd(String host, int port, int timeoutMs) throws Exception {
732         Socket s = new Socket(host, port);
733         s.setSoTimeout(timeoutMs);
734         // Dup the filedescriptor so ParcelFileDescriptor's finalizer doesn't garbage collect it
735         // and cause our fd to become invalid. http://b/35927643 .
736         FileDescriptor fd = Os.dup(ParcelFileDescriptor.fromSocket(s).getFileDescriptor());
737         s.close();
738         return fd;
739     }
740 
openSocketFdInOtherApp( String host, int port, int timeoutMs)741     private FileDescriptor openSocketFdInOtherApp(
742             String host, int port, int timeoutMs) throws Exception {
743         Log.d(TAG, String.format("Creating test socket in UID=%d, my UID=%d",
744                 mRemoteSocketFactoryClient.getUid(), Os.getuid()));
745         FileDescriptor fd = mRemoteSocketFactoryClient.openSocketFd(host, port, TIMEOUT_MS);
746         return fd;
747     }
748 
sendRequest(FileDescriptor fd, String host)749     private void sendRequest(FileDescriptor fd, String host) throws Exception {
750         String request = "GET /generate_204 HTTP/1.1\r\n" +
751                 "Host: " + host + "\r\n" +
752                 "Connection: keep-alive\r\n\r\n";
753         byte[] requestBytes = request.getBytes(StandardCharsets.UTF_8);
754         int ret = Os.write(fd, requestBytes, 0, requestBytes.length);
755         Log.d(TAG, "Wrote " + ret + "bytes");
756 
757         String expected = "HTTP/1.1 204 No Content\r\n";
758         byte[] response = new byte[expected.length()];
759         Os.read(fd, response, 0, response.length);
760 
761         String actual = new String(response, StandardCharsets.UTF_8);
762         assertEquals(expected, actual);
763         Log.d(TAG, "Got response: " + actual);
764     }
765 
assertSocketStillOpen(FileDescriptor fd, String host)766     private void assertSocketStillOpen(FileDescriptor fd, String host) throws Exception {
767         try {
768             assertTrue(fd.valid());
769             sendRequest(fd, host);
770             assertTrue(fd.valid());
771         } finally {
772             Os.close(fd);
773         }
774     }
775 
assertSocketClosed(FileDescriptor fd, String host)776     private void assertSocketClosed(FileDescriptor fd, String host) throws Exception {
777         try {
778             assertTrue(fd.valid());
779             sendRequest(fd, host);
780             fail("Socket opened before VPN connects should be closed when VPN connects");
781         } catch (ErrnoException expected) {
782             assertEquals(ECONNABORTED, expected.errno);
783             assertTrue(fd.valid());
784         } finally {
785             Os.close(fd);
786         }
787     }
788 
getContentResolver()789     private ContentResolver getContentResolver() {
790         return mTestContext.getContentResolver();
791     }
792 
isPrivateDnsInStrictMode()793     private boolean isPrivateDnsInStrictMode() {
794         return PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(
795                 Settings.Global.getString(getContentResolver(), PRIVATE_DNS_MODE_SETTING));
796     }
797 
storePrivateDnsSetting()798     private void storePrivateDnsSetting() {
799         mOldPrivateDnsMode = Settings.Global.getString(getContentResolver(),
800                 PRIVATE_DNS_MODE_SETTING);
801         mOldPrivateDnsSpecifier = Settings.Global.getString(getContentResolver(),
802                 PRIVATE_DNS_SPECIFIER_SETTING);
803     }
804 
restorePrivateDnsSetting()805     private void restorePrivateDnsSetting() {
806         Settings.Global.putString(getContentResolver(), PRIVATE_DNS_MODE_SETTING,
807                 mOldPrivateDnsMode);
808         Settings.Global.putString(getContentResolver(), PRIVATE_DNS_SPECIFIER_SETTING,
809                 mOldPrivateDnsSpecifier);
810     }
811 
812     // TODO: replace with CtsNetUtils.awaitPrivateDnsSetting in Q or above.
expectPrivateDnsHostname(final String hostname)813     private void expectPrivateDnsHostname(final String hostname) throws Exception {
814         final NetworkRequest request = new NetworkRequest.Builder()
815                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
816                 .build();
817         final CountDownLatch latch = new CountDownLatch(1);
818         final NetworkCallback callback = new NetworkCallback() {
819             @Override
820             public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
821                 if (network.equals(mNetwork) &&
822                         Objects.equals(lp.getPrivateDnsServerName(), hostname)) {
823                     latch.countDown();
824                 }
825             }
826         };
827 
828         registerNetworkCallback(request, callback);
829 
830         assertTrue("Private DNS hostname was not " + hostname + " after " + TIMEOUT_MS + "ms",
831                 latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
832     }
833 
setAndVerifyPrivateDns(boolean strictMode)834     private void setAndVerifyPrivateDns(boolean strictMode) throws Exception {
835         final ContentResolver cr = mTestContext.getContentResolver();
836         String privateDnsHostname;
837 
838         if (strictMode) {
839             privateDnsHostname = "vpncts-nx.metric.gstatic.com";
840             Settings.Global.putString(cr, PRIVATE_DNS_SPECIFIER_SETTING, privateDnsHostname);
841             Settings.Global.putString(cr, PRIVATE_DNS_MODE_SETTING,
842                     PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
843         } else {
844             Settings.Global.putString(cr, PRIVATE_DNS_MODE_SETTING, PRIVATE_DNS_MODE_OPPORTUNISTIC);
845             privateDnsHostname = null;
846         }
847 
848         expectPrivateDnsHostname(privateDnsHostname);
849 
850         String randomName = "vpncts-" + new Random().nextInt(1000000000) + "-ds.metric.gstatic.com";
851         if (strictMode) {
852             // Strict mode private DNS is enabled. DNS lookups should fail, because the private DNS
853             // server name is invalid.
854             try {
855                 InetAddress.getByName(randomName);
856                 fail("VPN DNS lookup should fail with private DNS enabled");
857             } catch (UnknownHostException expected) {
858             }
859         } else {
860             // Strict mode private DNS is disabled. DNS lookup should succeed, because the VPN
861             // provides no DNS servers, and thus DNS falls through to the default network.
862             assertNotNull("VPN DNS lookup should succeed with private DNS disabled",
863                     InetAddress.getByName(randomName));
864         }
865     }
866 
867     // Tests that strict mode private DNS is used on VPNs.
checkStrictModePrivateDns()868     private void checkStrictModePrivateDns() throws Exception {
869         final boolean initialMode = isPrivateDnsInStrictMode();
870         setAndVerifyPrivateDns(!initialMode);
871         setAndVerifyPrivateDns(initialMode);
872     }
873 
makeVpnNetworkRequest()874     private NetworkRequest makeVpnNetworkRequest() {
875         return new NetworkRequest.Builder()
876                 .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
877                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
878                 .build();
879     }
880 
expectUnderlyingNetworks(TestableNetworkCallback callback, @Nullable List<Network> expectUnderlyingNetworks)881     private void expectUnderlyingNetworks(TestableNetworkCallback callback,
882             @Nullable List<Network> expectUnderlyingNetworks) {
883         callback.eventuallyExpect(RecorderCallback.CallbackEntry.NETWORK_CAPS_UPDATED,
884                 NETWORK_CALLBACK_TIMEOUT_MS,
885                 entry -> (Objects.equals(expectUnderlyingNetworks,
886                         entry.getCaps().getUnderlyingNetworks())));
887     }
888 
expectVpnNetwork(TestableNetworkCallback callback)889     private void expectVpnNetwork(TestableNetworkCallback callback) {
890         callback.eventuallyExpect(RecorderCallback.CallbackEntry.NETWORK_CAPS_UPDATED,
891                 NETWORK_CALLBACK_TIMEOUT_MS,
892                 entry -> entry.getCaps().hasTransport(TRANSPORT_VPN));
893     }
894 
895     @Test @IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
testChangeUnderlyingNetworks()896     public void testChangeUnderlyingNetworks() throws Exception {
897         assumeTrue(supportedHardware());
898         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
899         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
900         final TestableNetworkCallback callback = new TestableNetworkCallback();
901         final boolean isWifiEnabled = mWifiManager.isWifiEnabled();
902         testAndCleanup(() -> {
903             // Ensure both of wifi and mobile data are connected.
904             final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
905             assertTrue("Wifi is not connected", (wifiNetwork != null));
906             final Network cellNetwork = mCtsNetUtils.connectToCell();
907             assertTrue("Mobile data is not connected", (cellNetwork != null));
908             // Store current default network.
909             final Network defaultNetwork = mCM.getActiveNetwork();
910             // Start VPN and set empty array as its underlying networks.
911             startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */,
912                     new String[] {"0.0.0.0/0", "::/0"} /* routes */,
913                     "" /* allowedApplications */, "" /* disallowedApplications */,
914                     null /* proxyInfo */, new ArrayList<>() /* underlyingNetworks */,
915                     false /* isAlwaysMetered */);
916             // Acquire the NETWORK_SETTINGS permission for getting the underlying networks.
917             runWithShellPermissionIdentity(() -> {
918                 registerNetworkCallback(makeVpnNetworkRequest(), callback);
919                 // Check that this VPN doesn't have any underlying networks.
920                 expectUnderlyingNetworks(callback, new ArrayList<Network>());
921 
922                 // Update the underlying networks to null and the underlying networks should follow
923                 // the system default network.
924                 updateUnderlyingNetworks(null);
925                 expectUnderlyingNetworks(callback, List.of(defaultNetwork));
926 
927                 // Update the underlying networks to mobile data.
928                 updateUnderlyingNetworks(new ArrayList<>(List.of(cellNetwork)));
929                 // Check the underlying networks of NetworkCapabilities which comes from
930                 // onCapabilitiesChanged is mobile data.
931                 expectUnderlyingNetworks(callback, List.of(cellNetwork));
932 
933                 // Update the underlying networks to wifi.
934                 updateUnderlyingNetworks(new ArrayList<>(List.of(wifiNetwork)));
935                 // Check the underlying networks of NetworkCapabilities which comes from
936                 // onCapabilitiesChanged is wifi.
937                 expectUnderlyingNetworks(callback, List.of(wifiNetwork));
938 
939                 // Update the underlying networks to wifi and mobile data.
940                 updateUnderlyingNetworks(new ArrayList<>(List.of(wifiNetwork, cellNetwork)));
941                 // Check the underlying networks of NetworkCapabilities which comes from
942                 // onCapabilitiesChanged is wifi and mobile data.
943                 expectUnderlyingNetworks(callback, List.of(wifiNetwork, cellNetwork));
944             }, NETWORK_SETTINGS);
945         }, () -> {
946                 if (isWifiEnabled) {
947                     mCtsNetUtils.ensureWifiConnected();
948                 } else {
949                     mCtsNetUtils.ensureWifiDisconnected(null);
950                 }
951             });
952     }
953 
954     @Test
testDefault()955     public void testDefault() throws Exception {
956         assumeTrue(supportedHardware());
957         if (!SdkLevel.isAtLeastS() && (
958                 SystemProperties.getInt("persist.adb.tcp.port", -1) > -1
959                         || SystemProperties.getInt("service.adb.tcp.port", -1) > -1)) {
960             // If adb TCP port opened, this test may running by adb over network.
961             // All of socket would be destroyed in this test. So this test don't
962             // support adb over network, see b/119382723.
963             // This is fixed in S, but still affects previous Android versions,
964             // and this test must be backwards compatible.
965             // TODO: Delete this code entirely when R is no longer supported.
966             Log.i(TAG, "adb is running over the network, so skip this test");
967             return;
968         }
969 
970         final BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver(
971                 mTargetContext, MyVpnService.ACTION_ESTABLISHED);
972         receiver.register();
973 
974         // Test the behaviour of a variety of types of network callbacks.
975         final Network defaultNetwork = mCM.getActiveNetwork();
976         final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback();
977         final TestableNetworkCallback otherUidCallback = new TestableNetworkCallback();
978         final TestableNetworkCallback myUidCallback = new TestableNetworkCallback();
979         if (SdkLevel.isAtLeastS()) {
980             final int otherUid =
981                     UserHandle.of(5 /* userId */).getUid(Process.FIRST_APPLICATION_UID);
982             final Handler h = new Handler(Looper.getMainLooper());
983             runWithShellPermissionIdentity(() -> {
984                 registerSystemDefaultNetworkCallback(systemDefaultCallback, h);
985                 registerDefaultNetworkCallbackForUid(otherUid, otherUidCallback, h);
986                 registerDefaultNetworkCallbackForUid(Process.myUid(), myUidCallback, h);
987             }, NETWORK_SETTINGS);
988             for (TestableNetworkCallback callback :
989                     List.of(systemDefaultCallback, otherUidCallback, myUidCallback)) {
990                 callback.expectAvailableCallbacks(defaultNetwork, false /* suspended */,
991                         true /* validated */, false /* blocked */, TIMEOUT_MS);
992             }
993         }
994 
995         FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
996 
997         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
998                  new String[] {"0.0.0.0/0", "::/0"},
999                  "", "", null, null /* underlyingNetworks */, false /* isAlwaysMetered */);
1000 
1001         final Intent intent = receiver.awaitForBroadcast(TimeUnit.MINUTES.toMillis(1));
1002         assertNotNull("Failed to receive broadcast from VPN service", intent);
1003         assertFalse("Wrong VpnService#isAlwaysOn",
1004                 intent.getBooleanExtra(MyVpnService.EXTRA_ALWAYS_ON, true));
1005         assertFalse("Wrong VpnService#isLockdownEnabled",
1006                 intent.getBooleanExtra(MyVpnService.EXTRA_LOCKDOWN_ENABLED, true));
1007 
1008         assertSocketClosed(fd, TEST_HOST);
1009 
1010         checkTrafficOnVpn();
1011 
1012         final Network vpnNetwork = mCM.getActiveNetwork();
1013         myUidCallback.expectAvailableThenValidatedCallbacks(vpnNetwork, TIMEOUT_MS);
1014         assertEquals(vpnNetwork, mCM.getActiveNetwork());
1015         assertNotEqual(defaultNetwork, vpnNetwork);
1016         maybeExpectVpnTransportInfo(vpnNetwork);
1017         assertEquals(TYPE_VPN, mCM.getNetworkInfo(vpnNetwork).getType());
1018 
1019         if (SdkLevel.isAtLeastT()) {
1020             runWithShellPermissionIdentity(() -> {
1021                 final NetworkCapabilities nc = mCM.getNetworkCapabilities(vpnNetwork);
1022                 assertNotNull(nc);
1023                 assertNotNull(nc.getUnderlyingNetworks());
1024                 assertEquals(defaultNetwork, new ArrayList<>(nc.getUnderlyingNetworks()).get(0));
1025             }, NETWORK_SETTINGS);
1026         }
1027 
1028         if (SdkLevel.isAtLeastS()) {
1029             // Check that system default network callback has not seen any network changes, even
1030             // though the app's default network changed. Also check that otherUidCallback saw no
1031             // network changes, because otherUid is in a different user and not subject to the VPN.
1032             // This needs to be done before testing  private DNS because checkStrictModePrivateDns
1033             // will set the private DNS server to a nonexistent name, which will cause validation to
1034             // fail and could cause the default network to switch (e.g., from wifi to cellular).
1035             systemDefaultCallback.assertNoCallback();
1036             otherUidCallback.assertNoCallback();
1037         }
1038 
1039         checkStrictModePrivateDns();
1040 
1041         receiver.unregisterQuietly();
1042     }
1043 
1044     @Test
testAppAllowed()1045     public void testAppAllowed() throws Exception {
1046         assumeTrue(supportedHardware());
1047 
1048         FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
1049 
1050         // Shell app must not be put in here or it would kill the ADB-over-network use case
1051         String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
1052         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
1053                  new String[] {"192.0.2.0/24", "2001:db8::/32"},
1054                  allowedApps, "", null, null /* underlyingNetworks */, false /* isAlwaysMetered */);
1055 
1056         assertSocketClosed(fd, TEST_HOST);
1057 
1058         checkTrafficOnVpn();
1059 
1060         maybeExpectVpnTransportInfo(mCM.getActiveNetwork());
1061 
1062         checkStrictModePrivateDns();
1063     }
1064 
getSupportedKeepalives(NetworkCapabilities nc)1065     private int getSupportedKeepalives(NetworkCapabilities nc) throws Exception {
1066         // Get number of supported concurrent keepalives for testing network.
1067         final int[] keepalivesPerTransport = KeepaliveUtils.getSupportedKeepalives(
1068                 mTargetContext);
1069         return KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(
1070                 keepalivesPerTransport, nc);
1071     }
1072 
1073     // This class can't be private, otherwise the constants can't be static imported.
1074     static class TestSocketKeepaliveCallback extends SocketKeepalive.Callback {
1075         // This must be larger than the alarm delay in AutomaticOnOffKeepaliveTracker.
1076         private static final int KEEPALIVE_TIMEOUT_MS = 10_000;
1077         public enum CallbackType {
1078             ON_STARTED,
1079             ON_RESUMED,
1080             ON_STOPPED,
1081             ON_PAUSED,
1082             ON_ERROR,
1083             ON_DATA_RECEIVED
1084         }
1085         private ArrayTrackRecord<CallbackType> mHistory = new ArrayTrackRecord<>();
1086         private ArrayTrackRecord<CallbackType>.ReadHead mEvents = mHistory.newReadHead();
1087 
1088         @Override
onStarted()1089         public void onStarted() {
1090             mHistory.add(ON_STARTED);
1091         }
1092 
1093         @Override
onResumed()1094         public void onResumed() {
1095             mHistory.add(ON_RESUMED);
1096         }
1097 
1098         @Override
onStopped()1099         public void onStopped() {
1100             mHistory.add(ON_STOPPED);
1101         }
1102 
1103         @Override
onPaused()1104         public void onPaused() {
1105             mHistory.add(ON_PAUSED);
1106         }
1107 
1108         @Override
onError(final int error)1109         public void onError(final int error) {
1110             mHistory.add(ON_ERROR);
1111         }
1112 
1113         @Override
onDataReceived()1114         public void onDataReceived() {
1115             mHistory.add(ON_DATA_RECEIVED);
1116         }
1117 
poll()1118         public CallbackType poll() {
1119             return mEvents.poll(KEEPALIVE_TIMEOUT_MS, it -> true);
1120         }
1121     }
1122 
getV4AddrByName(final String hostname)1123     private InetAddress getV4AddrByName(final String hostname) throws Exception {
1124         final InetAddress[] allAddrs = InetAddress.getAllByName(hostname);
1125         for (InetAddress addr : allAddrs) {
1126             if (addr instanceof Inet4Address) return addr;
1127         }
1128         return null;
1129     }
1130 
1131     @Test
testAutomaticOnOffKeepaliveModeNoClose()1132     public void testAutomaticOnOffKeepaliveModeNoClose() throws Exception {
1133         doTestAutomaticOnOffKeepaliveMode(false);
1134     }
1135 
1136     @Test
testAutomaticOnOffKeepaliveModeClose()1137     public void testAutomaticOnOffKeepaliveModeClose() throws Exception {
1138         doTestAutomaticOnOffKeepaliveMode(true);
1139     }
1140 
startKeepalive(SocketKeepalive kp, TestSocketKeepaliveCallback callback)1141     private void startKeepalive(SocketKeepalive kp, TestSocketKeepaliveCallback callback) {
1142         runWithShellPermissionIdentity(() -> {
1143             // Only SocketKeepalive.start() requires READ_DEVICE_CONFIG because feature is protected
1144             // by a feature flag. But also verify ON_STARTED callback received here to ensure
1145             // keepalive is indeed started because start() runs in the executor thread and shell
1146             // permission may be dropped before reading DeviceConfig.
1147             kp.start(10 /* intervalSec */, SocketKeepalive.FLAG_AUTOMATIC_ON_OFF, mNetwork);
1148 
1149             // Verify callback status.
1150             assertEquals(ON_STARTED, callback.poll());
1151         }, READ_DEVICE_CONFIG);
1152     }
1153 
doTestAutomaticOnOffKeepaliveMode(final boolean closeSocket)1154     private void doTestAutomaticOnOffKeepaliveMode(final boolean closeSocket) throws Exception {
1155         assumeTrue(supportedHardware());
1156 
1157         // Get default network first before starting VPN
1158         final Network defaultNetwork = mCM.getActiveNetwork();
1159         final TestableNetworkCallback cb = new TestableNetworkCallback();
1160         registerDefaultNetworkCallback(cb);
1161         cb.expect(CallbackEntry.AVAILABLE, defaultNetwork);
1162         final NetworkCapabilities cap =
1163                 cb.expect(CallbackEntry.NETWORK_CAPS_UPDATED, defaultNetwork).getCaps();
1164         final LinkProperties lp =
1165                 cb.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, defaultNetwork).getLp();
1166         cb.expect(CallbackEntry.BLOCKED_STATUS, defaultNetwork);
1167 
1168         // Setup VPN
1169         final FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
1170         final String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
1171         startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
1172                 new String[]{"192.0.2.0/24", "2001:db8::/32"},
1173                 allowedApps, "" /* disallowedApplications */, null /* proxyInfo */,
1174                 null /* underlyingNetworks */, false /* isAlwaysMetered */);
1175         assertSocketClosed(fd, TEST_HOST);
1176 
1177         // Decrease the TCP polling timer for testing.
1178         runWithShellPermissionIdentity(() -> mCM.setTestLowTcpPollingTimerForKeepalive(
1179                 System.currentTimeMillis() + TEST_TCP_POLLING_TIMER_EXPIRED_PERIOD_MS),
1180                 NETWORK_SETTINGS);
1181 
1182         // Setup keepalive
1183         final int supported = getSupportedKeepalives(cap);
1184         assumeTrue("Network " + defaultNetwork + " does not support keepalive", supported != 0);
1185         final InetAddress srcAddr = CollectionUtils.findFirst(lp.getAddresses(),
1186                 it -> it instanceof Inet4Address);
1187         assumeTrue("This test requires native IPv4", srcAddr != null);
1188 
1189         final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
1190 
1191         final String origMode = runWithShellPermissionIdentity(() -> {
1192             final String mode = DeviceConfig.getProperty(
1193                     DeviceConfig.NAMESPACE_CONNECTIVITY, AUTOMATIC_ON_OFF_KEEPALIVE_VERSION);
1194             DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CONNECTIVITY,
1195                     AUTOMATIC_ON_OFF_KEEPALIVE_VERSION,
1196                     AUTOMATIC_ON_OFF_KEEPALIVE_ENABLED, false /* makeDefault */);
1197             return mode;
1198         }, READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG);
1199 
1200         final IpSecManager ipSec = mTargetContext.getSystemService(IpSecManager.class);
1201         SocketKeepalive kp = null;
1202         try (IpSecManager.UdpEncapsulationSocket nattSocket = ipSec.openUdpEncapsulationSocket()) {
1203             final InetAddress dstAddr = getV4AddrByName(TEST_HOST);
1204             assertNotNull(dstAddr);
1205 
1206             // Start keepalive with dynamic keepalive mode enabled.
1207             final Executor executor = mTargetContext.getMainExecutor();
1208             kp = mCM.createSocketKeepalive(defaultNetwork, nattSocket,
1209                     srcAddr, dstAddr, executor, callback);
1210             startKeepalive(kp, callback);
1211 
1212             // There should be no open sockets on the VPN network, because any
1213             // open sockets were closed when startVpn above was called. So the
1214             // first TCP poll should trigger ON_PAUSED.
1215             assertEquals(ON_PAUSED, callback.poll());
1216 
1217             final Socket s = new Socket();
1218             mNetwork.bindSocket(s);
1219             s.connect(new InetSocketAddress(dstAddr, 80));
1220             assertEquals(ON_RESUMED, callback.poll());
1221 
1222             if (closeSocket) {
1223                 s.close();
1224                 assertEquals(ON_PAUSED, callback.poll());
1225             }
1226 
1227             kp.stop();
1228             assertEquals(ON_STOPPED, callback.poll());
1229         } finally {
1230             if (kp != null) kp.stop();
1231 
1232             runWithShellPermissionIdentity(() -> {
1233                 DeviceConfig.setProperty(
1234                                 DeviceConfig.NAMESPACE_CONNECTIVITY,
1235                                 AUTOMATIC_ON_OFF_KEEPALIVE_VERSION,
1236                                 origMode, false);
1237                 mCM.setTestLowTcpPollingTimerForKeepalive(0);
1238             }, WRITE_DEVICE_CONFIG, NETWORK_SETTINGS);
1239         }
1240     }
1241 
1242     @Test
testAppDisallowed()1243     public void testAppDisallowed() throws Exception {
1244         assumeTrue(supportedHardware());
1245 
1246         FileDescriptor localFd = openSocketFd(TEST_HOST, 80, TIMEOUT_MS);
1247         FileDescriptor remoteFd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
1248 
1249         String disallowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
1250         if (!SdkLevel.isAtLeastS()) {
1251             // If adb TCP port opened, this test may running by adb over TCP.
1252             // Add com.android.shell application into disallowedApps to exclude adb socket for VPN
1253             // test, see b/119382723 (the test doesn't support adb over TCP when adb runs as root).
1254             //
1255             // This is fixed in S, but still affects previous Android versions,
1256             // and this test must be backwards compatible.
1257             // TODO: Delete this code entirely when R is no longer supported.
1258             disallowedApps = disallowedApps + ",com.android.shell";
1259         }
1260         Log.i(TAG, "Append shell app to disallowedApps: " + disallowedApps);
1261         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
1262                 new String[] {"192.0.2.0/24", "2001:db8::/32"},
1263                 "", disallowedApps, null, null /* underlyingNetworks */,
1264                 false /* isAlwaysMetered */);
1265 
1266         assertSocketStillOpen(localFd, TEST_HOST);
1267         assertSocketStillOpen(remoteFd, TEST_HOST);
1268 
1269         checkNoTrafficOnVpn();
1270 
1271         final Network network = mCM.getActiveNetwork();
1272         final NetworkCapabilities nc = mCM.getNetworkCapabilities(network);
1273         assertFalse(nc.hasTransport(TRANSPORT_VPN));
1274     }
1275 
1276     @Test
testSocketClosed()1277     public void testSocketClosed() throws Exception {
1278         assumeTrue(supportedHardware());
1279 
1280         final FileDescriptor localFd = openSocketFd(TEST_HOST, 80, TIMEOUT_MS);
1281         final List<FileDescriptor> remoteFds = new ArrayList<>();
1282 
1283         for (int i = 0; i < 30; i++) {
1284             remoteFds.add(openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS));
1285         }
1286 
1287         final String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
1288         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
1289                 new String[] {"192.0.2.0/24", "2001:db8::/32"},
1290                 allowedApps, "", null, null /* underlyingNetworks */, false /* isAlwaysMetered */);
1291 
1292         // Socket owned by VPN uid is not closed
1293         assertSocketStillOpen(localFd, TEST_HOST);
1294 
1295         // Sockets not owned by VPN uid are closed
1296         for (final FileDescriptor remoteFd: remoteFds) {
1297             assertSocketClosed(remoteFd, TEST_HOST);
1298         }
1299     }
1300 
1301     @Test
testExcludedRoutes()1302     public void testExcludedRoutes() throws Exception {
1303         assumeTrue(supportedHardware());
1304         assumeTrue(SdkLevel.isAtLeastT());
1305 
1306         // Shell app must not be put in here or it would kill the ADB-over-network use case
1307         String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
1308         startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */,
1309                 new String[]{"0.0.0.0/0", "::/0"} /* routes */,
1310                 new String[]{"192.0.2.0/24", "2001:db8::/32"} /* excludedRoutes */,
1311                 allowedApps, "" /* disallowedApplications */, null /* proxyInfo */,
1312                 null /* underlyingNetworks */, false /* isAlwaysMetered */);
1313 
1314         // Excluded routes should bypass VPN.
1315         checkTrafficBypassesVpn("192.0.2.1");
1316         checkTrafficBypassesVpn("2001:db8:dead:beef::f00");
1317         // Other routes should go through VPN, since default routes are included.
1318         checkTrafficOnVpn("198.51.100.1");
1319         checkTrafficOnVpn("2002:db8::1");
1320     }
1321 
1322     @Test
testIncludedRoutes()1323     public void testIncludedRoutes() throws Exception {
1324         assumeTrue(supportedHardware());
1325 
1326         // Shell app must not be put in here or it would kill the ADB-over-network use case
1327         String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
1328         startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */,
1329                 new String[]{"192.0.2.0/24", "2001:db8::/32"} /* routes */,
1330                 allowedApps, "" /* disallowedApplications */, null /* proxyInfo */,
1331                 null /* underlyingNetworks */, false /* isAlwaysMetered */);
1332 
1333         // Included routes should go through VPN.
1334         checkTrafficOnVpn("192.0.2.1");
1335         checkTrafficOnVpn("2001:db8:dead:beef::f00");
1336         // Other routes should bypass VPN, since default routes are not included.
1337         checkTrafficBypassesVpn("198.51.100.1");
1338         checkTrafficBypassesVpn("2002:db8::1");
1339     }
1340 
1341     @Test
testInterleavedRoutes()1342     public void testInterleavedRoutes() throws Exception {
1343         assumeTrue(supportedHardware());
1344         assumeTrue(SdkLevel.isAtLeastT());
1345 
1346         // Shell app must not be put in here or it would kill the ADB-over-network use case
1347         String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
1348         startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */,
1349                 new String[]{"0.0.0.0/0", "192.0.2.0/32", "::/0", "2001:db8::/128"} /* routes */,
1350                 new String[]{"192.0.2.0/24", "2001:db8::/32"} /* excludedRoutes */,
1351                 allowedApps, "" /* disallowedApplications */, null /* proxyInfo */,
1352                 null /* underlyingNetworks */, false /* isAlwaysMetered */,
1353                 true /* addRoutesByIpPrefix */);
1354 
1355         // Excluded routes should bypass VPN.
1356         checkTrafficBypassesVpn("192.0.2.1");
1357         checkTrafficBypassesVpn("2001:db8:dead:beef::f00");
1358 
1359         // Included routes inside excluded routes should go through VPN, since the longest common
1360         // prefix precedes.
1361         checkTrafficOnVpn("192.0.2.0");
1362         checkTrafficOnVpn("2001:db8::");
1363 
1364         // Other routes should go through VPN, since default routes are included.
1365         checkTrafficOnVpn("198.51.100.1");
1366         checkTrafficOnVpn("2002:db8::1");
1367     }
1368 
1369     @Test
testGetConnectionOwnerUidSecurity()1370     public void testGetConnectionOwnerUidSecurity() throws Exception {
1371         assumeTrue(supportedHardware());
1372 
1373         DatagramSocket s;
1374         InetAddress address = InetAddress.getByName("localhost");
1375         s = new DatagramSocket();
1376         s.setSoTimeout(SOCKET_TIMEOUT_MS);
1377         s.connect(address, 7);
1378         InetSocketAddress loc = new InetSocketAddress(s.getLocalAddress(), s.getLocalPort());
1379         InetSocketAddress rem = new InetSocketAddress(s.getInetAddress(), s.getPort());
1380         try {
1381             int uid = mCM.getConnectionOwnerUid(OsConstants.IPPROTO_TCP, loc, rem);
1382             assertEquals("Only an active VPN app should see connection information",
1383                     INVALID_UID, uid);
1384         } catch (SecurityException acceptable) {
1385             // R and below throw SecurityException if a non-active VPN calls this method.
1386             // As long as we can't actually get socket information, either behaviour is fine.
1387             return;
1388         }
1389     }
1390 
1391     @Test
testSetProxy()1392     public void testSetProxy() throws  Exception {
1393         assumeTrue(supportedHardware());
1394         ProxyInfo initialProxy = mCM.getDefaultProxy();
1395         // Receiver for the proxy change broadcast.
1396         BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
1397         proxyBroadcastReceiver.register();
1398 
1399         String allowedApps = mPackageName;
1400         ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888);
1401         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
1402                 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "",
1403                 testProxyInfo, null /* underlyingNetworks */, false /* isAlwaysMetered */);
1404 
1405         // Check that the proxy change broadcast is received
1406         try {
1407             assertNotNull("No proxy change was broadcast when VPN is connected.",
1408                     proxyBroadcastReceiver.awaitForBroadcast());
1409         } finally {
1410             proxyBroadcastReceiver.unregisterQuietly();
1411         }
1412 
1413         // Proxy is set correctly in network and in link properties.
1414         assertNetworkHasExpectedProxy(testProxyInfo, mNetwork);
1415         assertDefaultProxy(testProxyInfo);
1416 
1417         proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
1418         proxyBroadcastReceiver.register();
1419         stopVpn();
1420         try {
1421             assertNotNull("No proxy change was broadcast when VPN was disconnected.",
1422                     proxyBroadcastReceiver.awaitForBroadcast());
1423         } finally {
1424             proxyBroadcastReceiver.unregisterQuietly();
1425         }
1426 
1427         // After disconnecting from VPN, the proxy settings are the ones of the initial network.
1428         assertDefaultProxy(initialProxy);
1429     }
1430 
1431     @Test
testSetProxyDisallowedApps()1432     public void testSetProxyDisallowedApps() throws Exception {
1433         assumeTrue(supportedHardware());
1434         ProxyInfo initialProxy = mCM.getDefaultProxy();
1435 
1436         String disallowedApps = mPackageName;
1437         if (!SdkLevel.isAtLeastS()) {
1438             // If adb TCP port opened, this test may running by adb over TCP.
1439             // Add com.android.shell application into disallowedApps to exclude adb socket for VPN
1440             // test, see b/119382723 (the test doesn't support adb over TCP when adb runs as root).
1441             //
1442             // This is fixed in S, but still affects previous Android versions,
1443             // and this test must be backwards compatible.
1444             // TODO: Delete this code entirely when R is no longer supported.
1445             disallowedApps += ",com.android.shell";
1446         }
1447         ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888);
1448         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
1449                 new String[] {"0.0.0.0/0", "::/0"}, "", disallowedApps,
1450                 testProxyInfo, null /* underlyingNetworks */, false /* isAlwaysMetered */);
1451 
1452         // The disallowed app does has the proxy configs of the default network.
1453         assertNetworkHasExpectedProxy(initialProxy, mCM.getActiveNetwork());
1454         assertDefaultProxy(initialProxy);
1455     }
1456 
1457     @Test
testNoProxy()1458     public void testNoProxy() throws Exception {
1459         assumeTrue(supportedHardware());
1460         ProxyInfo initialProxy = mCM.getDefaultProxy();
1461         BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
1462         proxyBroadcastReceiver.register();
1463         String allowedApps = mPackageName;
1464         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
1465                 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null,
1466                 null /* underlyingNetworks */, false /* isAlwaysMetered */);
1467 
1468         try {
1469             assertNotNull("No proxy change was broadcast.",
1470                     proxyBroadcastReceiver.awaitForBroadcast());
1471         } finally {
1472             proxyBroadcastReceiver.unregisterQuietly();
1473         }
1474 
1475         // The VPN network has no proxy set.
1476         assertNetworkHasExpectedProxy(null, mNetwork);
1477 
1478         proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
1479         proxyBroadcastReceiver.register();
1480         stopVpn();
1481         try {
1482             assertNotNull("No proxy change was broadcast.",
1483                     proxyBroadcastReceiver.awaitForBroadcast());
1484         } finally {
1485             proxyBroadcastReceiver.unregisterQuietly();
1486         }
1487         // After disconnecting from VPN, the proxy settings are the ones of the initial network.
1488         assertDefaultProxy(initialProxy);
1489         assertNetworkHasExpectedProxy(initialProxy, mCM.getActiveNetwork());
1490     }
1491 
1492     @Test
testBindToNetworkWithProxy()1493     public void testBindToNetworkWithProxy() throws Exception {
1494         assumeTrue(supportedHardware());
1495         String allowedApps = mPackageName;
1496         Network initialNetwork = mCM.getActiveNetwork();
1497         ProxyInfo initialProxy = mCM.getDefaultProxy();
1498         ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888);
1499         // Receiver for the proxy change broadcast.
1500         BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
1501         proxyBroadcastReceiver.register();
1502         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
1503                 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "",
1504                 testProxyInfo, null /* underlyingNetworks */, false /* isAlwaysMetered */);
1505 
1506         assertDefaultProxy(testProxyInfo);
1507         mCM.bindProcessToNetwork(initialNetwork);
1508         try {
1509             assertNotNull("No proxy change was broadcast.",
1510                 proxyBroadcastReceiver.awaitForBroadcast());
1511         } finally {
1512             proxyBroadcastReceiver.unregisterQuietly();
1513         }
1514         assertDefaultProxy(initialProxy);
1515     }
1516 
1517     @Test
testVpnMeterednessWithNoUnderlyingNetwork()1518     public void testVpnMeterednessWithNoUnderlyingNetwork() throws Exception {
1519         if (!supportedHardware()) {
1520             return;
1521         }
1522         // VPN is not routing any traffic i.e. its underlying networks is an empty array.
1523         ArrayList<Network> underlyingNetworks = new ArrayList<>();
1524         String allowedApps = mPackageName;
1525 
1526         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
1527                 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null,
1528                 underlyingNetworks, false /* isAlwaysMetered */);
1529 
1530         // VPN should now be the active network.
1531         assertEquals(mNetwork, mCM.getActiveNetwork());
1532         assertVpnTransportContains(NetworkCapabilities.TRANSPORT_VPN);
1533         // VPN with no underlying networks should be metered by default.
1534         assertTrue(isNetworkMetered(mNetwork));
1535         assertTrue(mCM.isActiveNetworkMetered());
1536 
1537         maybeExpectVpnTransportInfo(mCM.getActiveNetwork());
1538 
1539         if (SdkLevel.isAtLeastT()) {
1540             runWithShellPermissionIdentity(() -> {
1541                 final NetworkCapabilities nc = mCM.getNetworkCapabilities(mNetwork);
1542                 assertNotNull(nc);
1543                 assertNotNull(nc.getUnderlyingNetworks());
1544                 assertEquals(underlyingNetworks, new ArrayList<>(nc.getUnderlyingNetworks()));
1545             }, NETWORK_SETTINGS);
1546         }
1547     }
1548 
1549     @Test
testVpnMeterednessWithNullUnderlyingNetwork()1550     public void testVpnMeterednessWithNullUnderlyingNetwork() throws Exception {
1551         if (!supportedHardware()) {
1552             return;
1553         }
1554         Network underlyingNetwork = mCM.getActiveNetwork();
1555         if (underlyingNetwork == null) {
1556             Log.i(TAG, "testVpnMeterednessWithNullUnderlyingNetwork cannot execute"
1557                     + " unless there is an active network");
1558             return;
1559         }
1560         // VPN tracks platform default.
1561         ArrayList<Network> underlyingNetworks = null;
1562         String allowedApps = mPackageName;
1563 
1564         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
1565                 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null,
1566                 underlyingNetworks, false /*isAlwaysMetered */);
1567 
1568         // Ensure VPN transports contains underlying network's transports.
1569         assertVpnTransportContains(underlyingNetwork);
1570         // Its meteredness should be same as that of underlying network.
1571         assertEquals(isNetworkMetered(underlyingNetwork), isNetworkMetered(mNetwork));
1572         // Meteredness based on VPN capabilities and CM#isActiveNetworkMetered should be in sync.
1573         assertEquals(isNetworkMetered(mNetwork), mCM.isActiveNetworkMetered());
1574 
1575         maybeExpectVpnTransportInfo(mCM.getActiveNetwork());
1576     }
1577 
1578     @Test
testVpnMeterednessWithNonNullUnderlyingNetwork()1579     public void testVpnMeterednessWithNonNullUnderlyingNetwork() throws Exception {
1580         if (!supportedHardware()) {
1581             return;
1582         }
1583         Network underlyingNetwork = mCM.getActiveNetwork();
1584         if (underlyingNetwork == null) {
1585             Log.i(TAG, "testVpnMeterednessWithNonNullUnderlyingNetwork cannot execute"
1586                     + " unless there is an active network");
1587             return;
1588         }
1589         // VPN explicitly declares WiFi to be its underlying network.
1590         ArrayList<Network> underlyingNetworks = new ArrayList<>(1);
1591         underlyingNetworks.add(underlyingNetwork);
1592         String allowedApps = mPackageName;
1593 
1594         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
1595                 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null,
1596                 underlyingNetworks, false /* isAlwaysMetered */);
1597 
1598         // Ensure VPN transports contains underlying network's transports.
1599         assertVpnTransportContains(underlyingNetwork);
1600         // Its meteredness should be same as that of underlying network.
1601         assertEquals(isNetworkMetered(underlyingNetwork), isNetworkMetered(mNetwork));
1602         // Meteredness based on VPN capabilities and CM#isActiveNetworkMetered should be in sync.
1603         assertEquals(isNetworkMetered(mNetwork), mCM.isActiveNetworkMetered());
1604 
1605         maybeExpectVpnTransportInfo(mCM.getActiveNetwork());
1606 
1607         if (SdkLevel.isAtLeastT()) {
1608             final Network vpnNetwork = mCM.getActiveNetwork();
1609             assertNotEqual(underlyingNetwork, vpnNetwork);
1610             runWithShellPermissionIdentity(() -> {
1611                 final NetworkCapabilities nc = mCM.getNetworkCapabilities(vpnNetwork);
1612                 assertNotNull(nc);
1613                 assertNotNull(nc.getUnderlyingNetworks());
1614                 final List<Network> underlying = nc.getUnderlyingNetworks();
1615                 assertEquals(underlyingNetwork, underlying.get(0));
1616             }, NETWORK_SETTINGS);
1617         }
1618     }
1619 
1620     @Test
testAlwaysMeteredVpnWithNullUnderlyingNetwork()1621     public void testAlwaysMeteredVpnWithNullUnderlyingNetwork() throws Exception {
1622         if (!supportedHardware()) {
1623             return;
1624         }
1625         Network underlyingNetwork = mCM.getActiveNetwork();
1626         if (underlyingNetwork == null) {
1627             Log.i(TAG, "testAlwaysMeteredVpnWithNullUnderlyingNetwork cannot execute"
1628                     + " unless there is an active network");
1629             return;
1630         }
1631         // VPN tracks platform default.
1632         ArrayList<Network> underlyingNetworks = null;
1633         String allowedApps = mPackageName;
1634         boolean isAlwaysMetered = true;
1635 
1636         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
1637                 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null,
1638                 underlyingNetworks, isAlwaysMetered);
1639 
1640         // VPN's meteredness does not depend on underlying network since it is always metered.
1641         assertTrue(isNetworkMetered(mNetwork));
1642         assertTrue(mCM.isActiveNetworkMetered());
1643 
1644         maybeExpectVpnTransportInfo(mCM.getActiveNetwork());
1645     }
1646 
1647     @Test
testAlwaysMeteredVpnWithNonNullUnderlyingNetwork()1648     public void testAlwaysMeteredVpnWithNonNullUnderlyingNetwork() throws Exception {
1649         if (!supportedHardware()) {
1650             return;
1651         }
1652         Network underlyingNetwork = mCM.getActiveNetwork();
1653         if (underlyingNetwork == null) {
1654             Log.i(TAG, "testAlwaysMeteredVpnWithNonNullUnderlyingNetwork cannot execute"
1655                     + " unless there is an active network");
1656             return;
1657         }
1658         // VPN explicitly declares its underlying network.
1659         ArrayList<Network> underlyingNetworks = new ArrayList<>(1);
1660         underlyingNetworks.add(underlyingNetwork);
1661         String allowedApps = mPackageName;
1662         boolean isAlwaysMetered = true;
1663 
1664         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
1665                 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null,
1666                 underlyingNetworks, isAlwaysMetered);
1667 
1668         // VPN's meteredness does not depend on underlying network since it is always metered.
1669         assertTrue(isNetworkMetered(mNetwork));
1670         assertTrue(mCM.isActiveNetworkMetered());
1671 
1672         maybeExpectVpnTransportInfo(mCM.getActiveNetwork());
1673 
1674         if (SdkLevel.isAtLeastT()) {
1675             final Network vpnNetwork = mCM.getActiveNetwork();
1676             assertNotEqual(underlyingNetwork, vpnNetwork);
1677             runWithShellPermissionIdentity(() -> {
1678                 final NetworkCapabilities nc = mCM.getNetworkCapabilities(vpnNetwork);
1679                 assertNotNull(nc);
1680                 assertNotNull(nc.getUnderlyingNetworks());
1681                 final List<Network> underlying = nc.getUnderlyingNetworks();
1682                 assertEquals(underlyingNetwork, underlying.get(0));
1683             }, NETWORK_SETTINGS);
1684         }
1685     }
1686 
1687     @Test
testB141603906()1688     public void testB141603906() throws Exception {
1689         if (!supportedHardware()) {
1690             return;
1691         }
1692         final InetSocketAddress src = new InetSocketAddress(0);
1693         final InetSocketAddress dst = new InetSocketAddress(0);
1694         final int NUM_THREADS = 8;
1695         final int NUM_SOCKETS = 5000;
1696         final Thread[] threads = new Thread[NUM_THREADS];
1697         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
1698                  new String[] {"0.0.0.0/0", "::/0"},
1699                  "" /* allowedApplications */, "com.android.shell" /* disallowedApplications */,
1700                 null /* proxyInfo */, null /* underlyingNetworks */, false /* isAlwaysMetered */);
1701 
1702         for (int i = 0; i < NUM_THREADS; i++) {
1703             threads[i] = new Thread(() -> {
1704                 for (int j = 0; j < NUM_SOCKETS; j++) {
1705                     mCM.getConnectionOwnerUid(IPPROTO_TCP, src, dst);
1706                 }
1707             });
1708         }
1709         for (Thread thread : threads) {
1710             thread.start();
1711         }
1712         for (Thread thread : threads) {
1713             thread.join();
1714         }
1715         stopVpn();
1716     }
1717 
isNetworkMetered(Network network)1718     private boolean isNetworkMetered(Network network) {
1719         NetworkCapabilities nc = mCM.getNetworkCapabilities(network);
1720         return !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
1721     }
1722 
assertVpnTransportContains(Network underlyingNetwork)1723     private void assertVpnTransportContains(Network underlyingNetwork) {
1724         int[] transports = mCM.getNetworkCapabilities(underlyingNetwork).getTransportTypes();
1725         assertVpnTransportContains(transports);
1726     }
1727 
assertVpnTransportContains(int... transports)1728     private void assertVpnTransportContains(int... transports) {
1729         NetworkCapabilities vpnCaps = mCM.getNetworkCapabilities(mNetwork);
1730         for (int transport : transports) {
1731             assertTrue(vpnCaps.hasTransport(transport));
1732         }
1733     }
1734 
maybeExpectVpnTransportInfo(Network network)1735     private void maybeExpectVpnTransportInfo(Network network) {
1736         assumeTrue(SdkLevel.isAtLeastS());
1737         final NetworkCapabilities vpnNc = mCM.getNetworkCapabilities(network);
1738         assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
1739         final TransportInfo ti = vpnNc.getTransportInfo();
1740         assertTrue(ti instanceof VpnTransportInfo);
1741         assertEquals(VpnManager.TYPE_VPN_SERVICE, ((VpnTransportInfo) ti).getType());
1742     }
1743 
assertDefaultProxy(ProxyInfo expected)1744     private void assertDefaultProxy(ProxyInfo expected) {
1745         assertEquals("Incorrect proxy config.", expected, mCM.getDefaultProxy());
1746         String expectedHost = expected == null ? null : expected.getHost();
1747         String expectedPort = expected == null ? null : String.valueOf(expected.getPort());
1748         assertEquals("Incorrect proxy host system property.", expectedHost,
1749             System.getProperty("http.proxyHost"));
1750         assertEquals("Incorrect proxy port system property.", expectedPort,
1751             System.getProperty("http.proxyPort"));
1752     }
1753 
assertNetworkHasExpectedProxy(ProxyInfo expected, Network network)1754     private void assertNetworkHasExpectedProxy(ProxyInfo expected, Network network) {
1755         LinkProperties lp = mCM.getLinkProperties(network);
1756         assertNotNull("The network link properties object is null.", lp);
1757         assertEquals("Incorrect proxy config.", expected, lp.getHttpProxy());
1758 
1759         assertEquals(expected, mCM.getProxyForNetwork(network));
1760     }
1761 
1762     class ProxyChangeBroadcastReceiver extends BlockingBroadcastReceiver {
1763         private boolean received;
1764 
ProxyChangeBroadcastReceiver()1765         public ProxyChangeBroadcastReceiver() {
1766             super(mTestContext, Proxy.PROXY_CHANGE_ACTION);
1767             received = false;
1768         }
1769 
1770         @Override
onReceive(Context context, Intent intent)1771         public void onReceive(Context context, Intent intent) {
1772             if (!received) {
1773                 // Do not call onReceive() more than once.
1774                 super.onReceive(context, intent);
1775             }
1776             received = true;
1777         }
1778     }
1779 
1780     /**
1781      * Verifies that DownloadManager has CONNECTIVITY_USE_RESTRICTED_NETWORKS permission that can
1782      * bind socket to VPN when it is in VPN disallowed list but requested downloading app is in VPN
1783      * allowed list.
1784      * See b/165774987.
1785      */
1786     @Test
testDownloadWithDownloadManagerDisallowed()1787     public void testDownloadWithDownloadManagerDisallowed() throws Exception {
1788         assumeTrue(supportedHardware());
1789 
1790         // Start a VPN with DownloadManager package in disallowed list.
1791         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
1792                 new String[] {"192.0.2.0/24", "2001:db8::/32"},
1793                 "" /* allowedApps */, "com.android.providers.downloads", null /* proxyInfo */,
1794                 null /* underlyingNetworks */, false /* isAlwaysMetered */);
1795 
1796         final DownloadManager dm = mTestContext.getSystemService(DownloadManager.class);
1797         final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
1798         try {
1799             final int flags = SdkLevel.isAtLeastT() ? RECEIVER_EXPORTED : 0;
1800             mTestContext.registerReceiver(receiver,
1801                     new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE), flags);
1802 
1803             // Enqueue a request and check only one download.
1804             final long id = dm.enqueue(new Request(
1805                     Uri.parse("https://google-ipv6test.appspot.com/ip.js?fmt=text")));
1806             assertEquals(1, getTotalNumberDownloads(dm, new Query()));
1807             assertEquals(1, getTotalNumberDownloads(dm, new Query().setFilterById(id)));
1808 
1809             // Wait for download complete and check status.
1810             assertEquals(id, receiver.get(TIMEOUT_MS, TimeUnit.MILLISECONDS));
1811             assertEquals(1, getTotalNumberDownloads(dm,
1812                     new Query().setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL)));
1813 
1814             // Remove download.
1815             assertEquals(1, dm.remove(id));
1816             assertEquals(0, getTotalNumberDownloads(dm, new Query()));
1817         } finally {
1818             mTestContext.unregisterReceiver(receiver);
1819         }
1820     }
1821 
getTotalNumberDownloads(final DownloadManager dm, final Query query)1822     private static int getTotalNumberDownloads(final DownloadManager dm, final Query query) {
1823         try (Cursor cursor = dm.query(query)) { return cursor.getCount(); }
1824     }
1825 
1826     private static class DownloadCompleteReceiver extends BroadcastReceiver {
1827         private final CompletableFuture<Long> future = new CompletableFuture<>();
1828 
1829         @Override
onReceive(Context context, Intent intent)1830         public void onReceive(Context context, Intent intent) {
1831             future.complete(intent.getLongExtra(
1832                     DownloadManager.EXTRA_DOWNLOAD_ID, -1 /* defaultValue */));
1833         }
1834 
get(long timeout, TimeUnit unit)1835         public long get(long timeout, TimeUnit unit) throws Exception {
1836             return future.get(timeout, unit);
1837         }
1838     }
1839 
1840     private static final boolean EXPECT_PASS = false;
1841     private static final boolean EXPECT_BLOCK = true;
1842 
1843     @Test @IgnoreUpTo(Build.VERSION_CODES.R)
testBlockIncomingPackets()1844     public void testBlockIncomingPackets() throws Exception {
1845         assumeTrue(supportedHardware());
1846         final Network network = mCM.getActiveNetwork();
1847         assertNotNull("Requires a working Internet connection", network);
1848 
1849         final int remoteUid = mRemoteSocketFactoryClient.getUid();
1850         final List<Range<Integer>> lockdownRange = List.of(new Range<>(remoteUid, remoteUid));
1851         final DetailedBlockedStatusCallback remoteUidCallback = new DetailedBlockedStatusCallback();
1852 
1853         // Create a TUN interface
1854         final FileDescriptor tunFd = runWithShellPermissionIdentity(() -> {
1855             final TestNetworkManager tnm = mTestContext.getSystemService(TestNetworkManager.class);
1856             final TestNetworkInterface iface = tnm.createTunInterface(List.of(
1857                     TEST_IP4_DST_ADDR, TEST_IP6_DST_ADDR));
1858             return iface.getFileDescriptor().getFileDescriptor();
1859         }, MANAGE_TEST_NETWORKS);
1860 
1861         // Create a remote UDP socket
1862         final FileDescriptor remoteUdpFd = mRemoteSocketFactoryClient.openDatagramSocketFd();
1863 
1864         testAndCleanup(() -> {
1865             runWithShellPermissionIdentity(() -> {
1866                 registerDefaultNetworkCallbackForUid(remoteUid, remoteUidCallback,
1867                         new Handler(Looper.getMainLooper()));
1868             }, NETWORK_SETTINGS);
1869             remoteUidCallback.expectAvailableCallbacksWithBlockedReasonNone(network);
1870 
1871             // The remote UDP socket can receive packets coming from the TUN interface
1872             checkBlockIncomingPacket(tunFd, remoteUdpFd, EXPECT_PASS);
1873 
1874             // Lockdown uid that has the remote UDP socket
1875             runWithShellPermissionIdentity(() -> {
1876                 mCM.setRequireVpnForUids(true /* requireVpn */, lockdownRange);
1877             }, NETWORK_SETTINGS);
1878 
1879             // setRequireVpnForUids setup a lockdown rule asynchronously. So it needs to wait for
1880             // BlockedStatusCallback to be fired before checking the blocking status of incoming
1881             // packets.
1882             remoteUidCallback.expect(BLOCKED_STATUS_INT, network,
1883                     cb -> cb.getReason() == BLOCKED_REASON_LOCKDOWN_VPN);
1884 
1885             if (SdkLevel.isAtLeastT()) {
1886                 // On T and above, lockdown rule drop packets not coming from lo regardless of the
1887                 // VPN connectivity.
1888                 checkBlockIncomingPacket(tunFd, remoteUdpFd, EXPECT_BLOCK);
1889             }
1890 
1891             // Start the VPN that has default routes. This VPN should have interface filtering rule
1892             // for incoming packet and drop packets not coming from lo nor the VPN interface.
1893             final String allowedApps =
1894                     mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
1895             startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
1896                     new String[]{"0.0.0.0/0", "::/0"}, allowedApps, "" /* disallowedApplications */,
1897                     null /* proxyInfo */, null /* underlyingNetworks */,
1898                     false /* isAlwaysMetered */);
1899 
1900             checkBlockIncomingPacket(tunFd, remoteUdpFd, EXPECT_BLOCK);
1901         }, /* cleanup */ () -> {
1902                 Os.close(tunFd);
1903             }, /* cleanup */ () -> {
1904                 Os.close(remoteUdpFd);
1905             }, /* cleanup */ () -> {
1906                 runWithShellPermissionIdentity(() -> {
1907                     mCM.setRequireVpnForUids(false /* requireVpn */, lockdownRange);
1908                 }, NETWORK_SETTINGS);
1909             });
1910     }
1911 
1912     @Test
testSetVpnDefaultForUids()1913     public void testSetVpnDefaultForUids() throws Exception {
1914         assumeTrue(supportedHardware());
1915         assumeTrue(SdkLevel.isAtLeastU());
1916 
1917         final Network defaultNetwork = mCM.getActiveNetwork();
1918         assertNotNull("There must be a default network", defaultNetwork);
1919 
1920         final TestableNetworkCallback defaultNetworkCallback = new TestableNetworkCallback();
1921         final String session = UUID.randomUUID().toString();
1922         final int myUid = Process.myUid();
1923 
1924         testAndCleanup(() -> {
1925             registerDefaultNetworkCallback(defaultNetworkCallback);
1926             defaultNetworkCallback.expectAvailableCallbacks(defaultNetwork);
1927 
1928             final Range<Integer> myUidRange = new Range<>(myUid, myUid);
1929             runWithShellPermissionIdentity(() -> {
1930                 mCM.setVpnDefaultForUids(session, List.of(myUidRange));
1931             }, NETWORK_SETTINGS);
1932 
1933             // The VPN will be the only default network for the app, so it's expected to receive
1934             // onLost() callback.
1935             defaultNetworkCallback.eventuallyExpect(CallbackEntry.LOST);
1936 
1937             final ArrayList<Network> underlyingNetworks = new ArrayList<>();
1938             underlyingNetworks.add(defaultNetwork);
1939             startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */,
1940                     new String[] {"0.0.0.0/0", "::/0"} /* routes */,
1941                     "" /* allowedApplications */, "" /* disallowedApplications */,
1942                     null /* proxyInfo */, underlyingNetworks, false /* isAlwaysMetered */);
1943 
1944             expectVpnNetwork(defaultNetworkCallback);
1945         }, /* cleanup */ () -> {
1946                 stopVpn();
1947                 defaultNetworkCallback.eventuallyExpect(CallbackEntry.LOST);
1948             }, /* cleanup */ () -> {
1949                 runWithShellPermissionIdentity(() -> {
1950                     mCM.setVpnDefaultForUids(session, new ArraySet<>());
1951                 }, NETWORK_SETTINGS);
1952                 // The default network of the app will be changed back to wifi when the VPN network
1953                 // preference feature is disabled.
1954                 defaultNetworkCallback.eventuallyExpect(CallbackEntry.AVAILABLE,
1955                         NETWORK_CALLBACK_TIMEOUT_MS,
1956                         entry -> defaultNetwork.equals(entry.getNetwork()));
1957             });
1958     }
1959 
buildIpv4UdpPacket(final Inet4Address dstAddr, final Inet4Address srcAddr, final short dstPort, final short srcPort, final byte[] payload)1960     private ByteBuffer buildIpv4UdpPacket(final Inet4Address dstAddr, final Inet4Address srcAddr,
1961             final short dstPort, final short srcPort, final byte[] payload) throws IOException {
1962 
1963         final ByteBuffer buffer = PacketBuilder.allocate(false /* hasEther */,
1964                 OsConstants.IPPROTO_IP, OsConstants.IPPROTO_UDP, payload.length);
1965         final PacketBuilder packetBuilder = new PacketBuilder(buffer);
1966 
1967         packetBuilder.writeIpv4Header(
1968                 (byte) 0 /* TOS */,
1969                 (short) 27149 /* ID */,
1970                 (short) 0x4000 /* flags=DF, offset=0 */,
1971                 (byte) 64 /* TTL */,
1972                 (byte) OsConstants.IPPROTO_UDP,
1973                 srcAddr,
1974                 dstAddr);
1975         packetBuilder.writeUdpHeader(srcPort, dstPort);
1976         buffer.put(payload);
1977 
1978         return packetBuilder.finalizePacket();
1979     }
1980 
buildIpv6UdpPacket(final Inet6Address dstAddr, final Inet6Address srcAddr, final short dstPort, final short srcPort, final byte[] payload)1981     private ByteBuffer buildIpv6UdpPacket(final Inet6Address dstAddr, final Inet6Address srcAddr,
1982             final short dstPort, final short srcPort, final byte[] payload) throws IOException {
1983 
1984         final ByteBuffer buffer = PacketBuilder.allocate(false /* hasEther */,
1985                 OsConstants.IPPROTO_IPV6, OsConstants.IPPROTO_UDP, payload.length);
1986         final PacketBuilder packetBuilder = new PacketBuilder(buffer);
1987 
1988         packetBuilder.writeIpv6Header(
1989                 0x60000000 /* version=6, traffic class=0, flow label=0 */,
1990                 (byte) OsConstants.IPPROTO_UDP,
1991                 (short) 64 /* hop limit */,
1992                 srcAddr,
1993                 dstAddr);
1994         packetBuilder.writeUdpHeader(srcPort, dstPort);
1995         buffer.put(payload);
1996 
1997         return packetBuilder.finalizePacket();
1998     }
1999 
checkBlockUdp( final FileDescriptor srcTunFd, final FileDescriptor dstUdpFd, final boolean ipv6, final boolean expectBlock)2000     private void checkBlockUdp(
2001             final FileDescriptor srcTunFd,
2002             final FileDescriptor dstUdpFd,
2003             final boolean ipv6,
2004             final boolean expectBlock) throws Exception {
2005         final Random random = new Random();
2006         final byte[] sendData = new byte[100];
2007         random.nextBytes(sendData);
2008         final short dstPort = (short) ((InetSocketAddress) Os.getsockname(dstUdpFd)).getPort();
2009 
2010         ByteBuffer buf;
2011         if (ipv6) {
2012             buf = buildIpv6UdpPacket(
2013                     (Inet6Address) TEST_IP6_DST_ADDR.getAddress(),
2014                     (Inet6Address) TEST_IP6_SRC_ADDR.getAddress(),
2015                     dstPort, TEST_SRC_PORT, sendData);
2016         } else {
2017             buf = buildIpv4UdpPacket(
2018                     (Inet4Address) TEST_IP4_DST_ADDR.getAddress(),
2019                     (Inet4Address) TEST_IP4_SRC_ADDR.getAddress(),
2020                     dstPort, TEST_SRC_PORT, sendData);
2021         }
2022 
2023         Os.write(srcTunFd, buf);
2024 
2025         final StructPollfd pollfd = new StructPollfd();
2026         pollfd.events = (short) POLLIN;
2027         pollfd.fd = dstUdpFd;
2028         final int ret = Os.poll(new StructPollfd[]{pollfd}, SOCKET_TIMEOUT_MS);
2029 
2030         if (expectBlock) {
2031             assertEquals("Expect not to receive a packet but received a packet", 0, ret);
2032         } else {
2033             assertEquals("Expect to receive a packet but did not receive a packet", 1, ret);
2034             final byte[] recvData = new byte[sendData.length];
2035             final int readSize = Os.read(dstUdpFd, recvData, 0 /* byteOffset */, recvData.length);
2036             assertEquals(recvData.length, readSize);
2037             MoreAsserts.assertEquals(sendData, recvData);
2038         }
2039     }
2040 
checkBlockIncomingPacket( final FileDescriptor srcTunFd, final FileDescriptor dstUdpFd, final boolean expectBlock)2041     private void checkBlockIncomingPacket(
2042             final FileDescriptor srcTunFd,
2043             final FileDescriptor dstUdpFd,
2044             final boolean expectBlock) throws Exception {
2045         checkBlockUdp(srcTunFd, dstUdpFd, false /* ipv6 */, expectBlock);
2046         checkBlockUdp(srcTunFd, dstUdpFd, true /* ipv6 */, expectBlock);
2047     }
2048 
2049     private class DetailedBlockedStatusCallback extends TestableNetworkCallback {
expectAvailableCallbacksWithBlockedReasonNone(Network network)2050         public void expectAvailableCallbacksWithBlockedReasonNone(Network network) {
2051             super.expectAvailableCallbacks(network, false /* suspended */, true /* validated */,
2052                     BLOCKED_REASON_NONE, NETWORK_CALLBACK_TIMEOUT_MS);
2053         }
onBlockedStatusChanged(Network network, int blockedReasons)2054         public void onBlockedStatusChanged(Network network, int blockedReasons) {
2055             getHistory().add(new CallbackEntry.BlockedStatusInt(network, blockedReasons));
2056         }
2057     }
2058 }
2059