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