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