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