1 /*
2  * Copyright (C) 2009 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 android.net.cts;
18 
19 import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
20 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
21 import static android.Manifest.permission.ACCESS_NETWORK_STATE;
22 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
23 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
24 import static android.Manifest.permission.NETWORK_FACTORY;
25 import static android.Manifest.permission.NETWORK_SETTINGS;
26 import static android.Manifest.permission.NETWORK_SETUP_WIZARD;
27 import static android.Manifest.permission.NETWORK_STACK;
28 import static android.Manifest.permission.READ_DEVICE_CONFIG;
29 import static android.Manifest.permission.TETHER_PRIVILEGED;
30 import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
31 import static android.content.pm.PackageManager.FEATURE_ETHERNET;
32 import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
33 import static android.content.pm.PackageManager.FEATURE_USB_HOST;
34 import static android.content.pm.PackageManager.FEATURE_WATCH;
35 import static android.content.pm.PackageManager.FEATURE_WIFI;
36 import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
37 import static android.content.pm.PackageManager.GET_PERMISSIONS;
38 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
39 import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_ADMIN_DISABLED;
40 import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED;
41 import static android.net.ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND;
42 import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY;
43 import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER;
44 import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE;
45 import static android.net.ConnectivityManager.BLOCKED_REASON_LOW_POWER_STANDBY;
46 import static android.net.ConnectivityManager.BLOCKED_REASON_OEM_DENY;
47 import static android.net.ConnectivityManager.BLOCKED_REASON_RESTRICTED_MODE;
48 import static android.net.ConnectivityManager.EXTRA_NETWORK;
49 import static android.net.ConnectivityManager.EXTRA_NETWORK_REQUEST;
50 import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
51 import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
52 import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
53 import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
54 import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
55 import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_1;
56 import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_2;
57 import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_3;
58 import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
59 import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
60 import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
61 import static android.net.ConnectivityManager.FIREWALL_RULE_ALLOW;
62 import static android.net.ConnectivityManager.FIREWALL_RULE_DEFAULT;
63 import static android.net.ConnectivityManager.FIREWALL_RULE_DENY;
64 import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
65 import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
66 import static android.net.ConnectivityManager.TYPE_ETHERNET;
67 import static android.net.ConnectivityManager.TYPE_MOBILE_CBS;
68 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
69 import static android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY;
70 import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
71 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
72 import static android.net.ConnectivityManager.TYPE_MOBILE_IA;
73 import static android.net.ConnectivityManager.TYPE_MOBILE_IMS;
74 import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
75 import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
76 import static android.net.ConnectivityManager.TYPE_PROXY;
77 import static android.net.ConnectivityManager.TYPE_VPN;
78 import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
79 import static android.net.ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks;
80 import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
81 import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
82 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
83 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
84 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
85 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
86 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
87 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
88 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
89 import static android.net.NetworkCapabilities.TRANSPORT_TEST;
90 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
91 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
92 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
93 import static android.net.cts.util.CtsNetUtils.ConnectivityActionReceiver;
94 import static android.net.cts.util.CtsNetUtils.HTTP_PORT;
95 import static android.net.cts.util.CtsNetUtils.NETWORK_CALLBACK_ACTION;
96 import static android.net.cts.util.CtsNetUtils.TEST_HOST;
97 import static android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback;
98 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
99 import static android.os.Process.INVALID_UID;
100 import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
101 import static android.system.OsConstants.AF_INET;
102 import static android.system.OsConstants.AF_INET6;
103 import static android.system.OsConstants.AF_UNSPEC;
104 
105 import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
106 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
107 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
108 import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
109 import static com.android.net.module.util.NetworkStackConstants.TEST_CAPTIVE_PORTAL_HTTPS_URL;
110 import static com.android.net.module.util.NetworkStackConstants.TEST_CAPTIVE_PORTAL_HTTP_URL;
111 import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_LOCKDOWN_VPN;
112 import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_NONE;
113 import static com.android.networkstack.apishim.ConstantsShim.RECEIVER_EXPORTED;
114 import static com.android.testutils.Cleanup.testAndCleanup;
115 import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
116 import static com.android.testutils.MiscAsserts.assertThrows;
117 import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
118 import static com.android.testutils.TestPermissionUtil.runAsShell;
119 
120 import static org.junit.Assert.assertArrayEquals;
121 import static org.junit.Assert.assertEquals;
122 import static org.junit.Assert.assertFalse;
123 import static org.junit.Assert.assertNotEquals;
124 import static org.junit.Assert.assertNotNull;
125 import static org.junit.Assert.assertNotSame;
126 import static org.junit.Assert.assertNull;
127 import static org.junit.Assert.assertTrue;
128 import static org.junit.Assert.fail;
129 import static org.junit.Assume.assumeTrue;
130 
131 import android.annotation.NonNull;
132 import android.app.Instrumentation;
133 import android.app.PendingIntent;
134 import android.app.UiAutomation;
135 import android.content.BroadcastReceiver;
136 import android.content.ContentResolver;
137 import android.content.Context;
138 import android.content.Intent;
139 import android.content.IntentFilter;
140 import android.content.pm.PackageInfo;
141 import android.content.pm.PackageManager;
142 import android.content.res.Resources;
143 import android.net.CaptivePortalData;
144 import android.net.ConnectivityManager;
145 import android.net.ConnectivityManager.NetworkCallback;
146 import android.net.ConnectivitySettingsManager;
147 import android.net.InetAddresses;
148 import android.net.IpSecManager;
149 import android.net.IpSecManager.UdpEncapsulationSocket;
150 import android.net.LinkAddress;
151 import android.net.LinkProperties;
152 import android.net.Network;
153 import android.net.NetworkAgent;
154 import android.net.NetworkAgentConfig;
155 import android.net.NetworkCapabilities;
156 import android.net.NetworkInfo;
157 import android.net.NetworkInfo.DetailedState;
158 import android.net.NetworkInfo.State;
159 import android.net.NetworkProvider;
160 import android.net.NetworkRequest;
161 import android.net.NetworkSpecifier;
162 import android.net.NetworkStateSnapshot;
163 import android.net.OemNetworkPreferences;
164 import android.net.ProxyInfo;
165 import android.net.SocketKeepalive;
166 import android.net.TelephonyNetworkSpecifier;
167 import android.net.TestNetworkInterface;
168 import android.net.TestNetworkManager;
169 import android.net.Uri;
170 import android.net.cts.util.CtsNetUtils;
171 import android.net.cts.util.CtsTetheringUtils;
172 import android.net.util.KeepaliveUtils;
173 import android.net.wifi.WifiInfo;
174 import android.net.wifi.WifiManager;
175 import android.os.Binder;
176 import android.os.Build;
177 import android.os.Bundle;
178 import android.os.Handler;
179 import android.os.Looper;
180 import android.os.MessageQueue;
181 import android.os.Process;
182 import android.os.ServiceManager;
183 import android.os.SystemClock;
184 import android.os.UserHandle;
185 import android.os.VintfRuntimeInfo;
186 import android.platform.test.annotations.AppModeFull;
187 import android.provider.DeviceConfig;
188 import android.provider.Settings;
189 import android.telephony.SubscriptionManager;
190 import android.telephony.TelephonyManager;
191 import android.text.TextUtils;
192 import android.util.ArraySet;
193 import android.util.Log;
194 import android.util.Range;
195 
196 import androidx.test.filters.RequiresDevice;
197 import androidx.test.platform.app.InstrumentationRegistry;
198 import androidx.test.runner.AndroidJUnit4;
199 
200 import com.android.compatibility.common.util.DynamicConfigDeviceSide;
201 import com.android.internal.util.ArrayUtils;
202 import com.android.modules.utils.build.SdkLevel;
203 import com.android.net.module.util.CollectionUtils;
204 import com.android.networkstack.apishim.ConnectivityManagerShimImpl;
205 import com.android.networkstack.apishim.ConstantsShim;
206 import com.android.networkstack.apishim.NetworkInformationShimImpl;
207 import com.android.networkstack.apishim.common.ConnectivityManagerShim;
208 import com.android.testutils.AutoReleaseNetworkCallbackRule;
209 import com.android.testutils.CompatUtil;
210 import com.android.testutils.ConnectivityModuleTest;
211 import com.android.testutils.DevSdkIgnoreRule;
212 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
213 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
214 import com.android.testutils.DeviceConfigRule;
215 import com.android.testutils.DeviceInfoUtils;
216 import com.android.testutils.DumpTestUtils;
217 import com.android.testutils.RecorderCallback.CallbackEntry;
218 import com.android.testutils.SkipMainlinePresubmit;
219 import com.android.testutils.SkipPresubmit;
220 import com.android.testutils.TestHttpServer;
221 import com.android.testutils.TestNetworkTracker;
222 import com.android.testutils.TestableNetworkCallback;
223 
224 import junit.framework.AssertionFailedError;
225 
226 import libcore.io.Streams;
227 
228 import org.junit.After;
229 import org.junit.Before;
230 import org.junit.Ignore;
231 import org.junit.Rule;
232 import org.junit.Test;
233 import org.junit.runner.RunWith;
234 
235 import java.io.FileDescriptor;
236 import java.io.IOException;
237 import java.io.InputStream;
238 import java.io.InputStreamReader;
239 import java.io.OutputStream;
240 import java.net.DatagramPacket;
241 import java.net.DatagramSocket;
242 import java.net.HttpURLConnection;
243 import java.net.Inet4Address;
244 import java.net.Inet6Address;
245 import java.net.InetAddress;
246 import java.net.InetSocketAddress;
247 import java.net.MalformedURLException;
248 import java.net.Socket;
249 import java.net.SocketException;
250 import java.net.URL;
251 import java.net.UnknownHostException;
252 import java.nio.charset.StandardCharsets;
253 import java.util.ArrayList;
254 import java.util.Arrays;
255 import java.util.Collection;
256 import java.util.List;
257 import java.util.Objects;
258 import java.util.Random;
259 import java.util.Set;
260 import java.util.UUID;
261 import java.util.concurrent.CompletableFuture;
262 import java.util.concurrent.CountDownLatch;
263 import java.util.concurrent.Executor;
264 import java.util.concurrent.ExecutorService;
265 import java.util.concurrent.Executors;
266 import java.util.concurrent.LinkedBlockingQueue;
267 import java.util.concurrent.TimeUnit;
268 import java.util.concurrent.TimeoutException;
269 import java.util.concurrent.atomic.AtomicInteger;
270 import java.util.function.Supplier;
271 import java.util.regex.Matcher;
272 import java.util.regex.Pattern;
273 
274 import fi.iki.elonen.NanoHTTPD.Method;
275 import fi.iki.elonen.NanoHTTPD.Response.IStatus;
276 import fi.iki.elonen.NanoHTTPD.Response.Status;
277 
278 @RunWith(AndroidJUnit4.class)
279 public class ConnectivityManagerTest {
280     @Rule(order = 1)
281     public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
282 
283     @Rule(order = 2)
284     public final AutoReleaseNetworkCallbackRule
285             networkCallbackRule = new AutoReleaseNetworkCallbackRule();
286 
287     @Rule(order = 3)
288     public final DeviceConfigRule mTestValidationConfigRule = new DeviceConfigRule(
289             5 /* retryCountBeforeSIfConfigChanged */);
290 
291     private static final String TAG = ConnectivityManagerTest.class.getSimpleName();
292 
293     public static final int TYPE_MOBILE = ConnectivityManager.TYPE_MOBILE;
294     public static final int TYPE_WIFI = ConnectivityManager.TYPE_WIFI;
295 
296     private static final int HOST_ADDRESS = 0x7f000001;// represent ip 127.0.0.1
297     private static final int KEEPALIVE_CALLBACK_TIMEOUT_MS = 2000;
298     private static final int INTERVAL_KEEPALIVE_RETRY_MS = 500;
299     private static final int MAX_KEEPALIVE_RETRY_COUNT = 3;
300     private static final int MIN_KEEPALIVE_INTERVAL = 10;
301 
302     private static final int NETWORK_CALLBACK_TIMEOUT_MS = 30_000;
303     // Timeout for waiting network to be validated.
304     private static final int LISTEN_ACTIVITY_TIMEOUT_MS = 30_000;
305     private static final int NO_CALLBACK_TIMEOUT_MS = 100;
306     private static final int NETWORK_REQUEST_TIMEOUT_MS = 3000;
307     private static final int SOCKET_TIMEOUT_MS = 100;
308     private static final int NUM_TRIES_MULTIPATH_PREF_CHECK = 20;
309     private static final long INTERVAL_MULTIPATH_PREF_CHECK_MS = 500;
310     // device could have only one interface: data, wifi.
311     private static final int MIN_NUM_NETWORK_TYPES = 1;
312 
313     // Airplane Mode BroadcastReceiver Timeout
314     private static final long AIRPLANE_MODE_CHANGE_TIMEOUT_MS = 10_000L;
315     private static final long CELL_DATA_AVAILABLE_TIMEOUT_MS = 120_000L;
316 
317     // Timeout for applying uids allowed on restricted networks
318     private static final long APPLYING_UIDS_ALLOWED_ON_RESTRICTED_NETWORKS_TIMEOUT_MS = 3_000L;
319 
320     // Minimum supported keepalive counts for wifi and cellular.
321     public static final int MIN_SUPPORTED_CELLULAR_KEEPALIVE_COUNT = 1;
322     public static final int MIN_SUPPORTED_WIFI_KEEPALIVE_COUNT = 3;
323 
324     private static final String NETWORK_METERED_MULTIPATH_PREFERENCE_RES_NAME =
325             "config_networkMeteredMultipathPreference";
326     private static final String KEEPALIVE_ALLOWED_UNPRIVILEGED_RES_NAME =
327             "config_allowedUnprivilegedKeepalivePerUid";
328     private static final String KEEPALIVE_RESERVED_PER_SLOT_RES_NAME =
329             "config_reservedPrivilegedKeepaliveSlots";
330     private static final String TEST_RESTRICTED_NW_IFACE_NAME = "test-restricted-nw";
331 
332     private static final LinkAddress TEST_LINKADDR = new LinkAddress(
333             InetAddresses.parseNumericAddress("2001:db8::8"), 64);
334 
335     private static final int AIRPLANE_MODE_OFF = 0;
336     private static final int AIRPLANE_MODE_ON = 1;
337 
338     private static final String TEST_HTTPS_URL_PATH = "/https_path";
339     private static final String TEST_HTTP_URL_PATH = "/http_path";
340     private static final String LOCALHOST_HOSTNAME = "localhost";
341     private static final String TEST_MODULE_NAME_OPTION = "test-module-name";
342     private static final String IP_ADDRESS_ECHO_URL_KEY = "IP_ADDRESS_ECHO_URL";
343     private static final List<String> ALLOWED_IP_ADDRESS_ECHO_URLS = Arrays.asList(
344             "https://google-ipv6test.appspot.com/ip.js?fmt=text",
345             "https://ipv6test.googleapis-cn.com/ip.js?fmt=text");
346     // Re-connecting to the AP, obtaining an IP address, revalidating can take a long time
347     private static final long WIFI_CONNECT_TIMEOUT_MS = 60_000L;
348 
349     private Context mContext;
350     private Instrumentation mInstrumentation;
351     private ConnectivityManager mCm;
352     private ConnectivityManagerShim mCmShim;
353     private WifiManager mWifiManager;
354     private PackageManager mPackageManager;
355     private TelephonyManager mTm;
356     private final ArraySet<Integer> mNetworkTypes = new ArraySet<>();
357     private UiAutomation mUiAutomation;
358     private CtsNetUtils mCtsNetUtils;
359     // Used for cleanup purposes.
360     private final List<Range<Integer>> mVpnRequiredUidRanges = new ArrayList<>();
361 
362     private final TestHttpServer mHttpServer = new TestHttpServer(LOCALHOST_HOSTNAME);
363 
364     @Before
setUp()365     public void setUp() throws Exception {
366         mInstrumentation = InstrumentationRegistry.getInstrumentation();
367         mContext = mInstrumentation.getContext();
368         mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
369         mCmShim = ConnectivityManagerShimImpl.newInstance(mContext);
370         mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
371         mPackageManager = mContext.getPackageManager();
372         mCtsNetUtils = new CtsNetUtils(mContext);
373         mTm = mContext.getSystemService(TelephonyManager.class);
374 
375         if (isAtLeastS()) {
376             addSupportedNetworkTypes();
377         } else {
378             addLegacySupportedNetworkTypes();
379         }
380 
381         mUiAutomation = mInstrumentation.getUiAutomation();
382 
383         assertNotNull("CTS requires a working Internet connection", mCm.getActiveNetwork());
384     }
385 
addLegacySupportedNetworkTypes()386     private void addLegacySupportedNetworkTypes() {
387         // Network type support as expected for android R-
388         // Get com.android.internal.R.array.networkAttributes
389         int resId = mContext.getResources().getIdentifier("networkAttributes", "array", "android");
390         String[] naStrings = mContext.getResources().getStringArray(resId);
391         boolean wifiOnly = mPackageManager.hasSystemFeature(FEATURE_WIFI)
392                 && !mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
393         for (String naString : naStrings) {
394             try {
395                 final String[] splitConfig = naString.split(",");
396                 // Format was name,type,radio,priority,restoreTime,dependencyMet
397                 final int type = Integer.parseInt(splitConfig[1]);
398                 if (wifiOnly && ConnectivityManager.isNetworkTypeMobile(type)) {
399                     continue;
400                 }
401                 mNetworkTypes.add(type);
402             } catch (Exception e) {}
403         }
404     }
405 
addSupportedNetworkTypes()406     private void addSupportedNetworkTypes() {
407         final PackageManager pm = mContext.getPackageManager();
408         if (pm.hasSystemFeature(FEATURE_WIFI)) {
409             mNetworkTypes.add(TYPE_WIFI);
410         }
411         if (pm.hasSystemFeature(FEATURE_WIFI_DIRECT)) {
412             mNetworkTypes.add(TYPE_WIFI_P2P);
413         }
414         if (mContext.getSystemService(TelephonyManager.class).isDataCapable()) {
415             mNetworkTypes.add(TYPE_MOBILE);
416             mNetworkTypes.add(TYPE_MOBILE_MMS);
417             mNetworkTypes.add(TYPE_MOBILE_SUPL);
418             mNetworkTypes.add(TYPE_MOBILE_DUN);
419             mNetworkTypes.add(TYPE_MOBILE_HIPRI);
420             mNetworkTypes.add(TYPE_MOBILE_FOTA);
421             mNetworkTypes.add(TYPE_MOBILE_IMS);
422             mNetworkTypes.add(TYPE_MOBILE_CBS);
423             mNetworkTypes.add(TYPE_MOBILE_IA);
424             mNetworkTypes.add(TYPE_MOBILE_EMERGENCY);
425         }
426         if (pm.hasSystemFeature(FEATURE_BLUETOOTH)) {
427             mNetworkTypes.add(TYPE_BLUETOOTH);
428         }
429         if (pm.hasSystemFeature(FEATURE_WATCH)) {
430             mNetworkTypes.add(TYPE_PROXY);
431         }
432         if (mContext.getSystemService(Context.ETHERNET_SERVICE) != null) {
433             mNetworkTypes.add(TYPE_ETHERNET);
434         }
435         mNetworkTypes.add(TYPE_VPN);
436     }
437 
438     @After
tearDown()439     public void tearDown() throws Exception {
440         if (TestUtils.shouldTestSApis()) {
441             runWithShellPermissionIdentity(
442                     () -> mCmShim.setRequireVpnForUids(false, mVpnRequiredUidRanges),
443                     NETWORK_SETTINGS);
444         }
445 
446         // All tests in this class require a working Internet connection as they start. Make
447         // sure there is still one as they end that's ready to use for the next test to use.
448         mTestValidationConfigRule.runAfterNextCleanup(() -> {
449             // mTestValidationConfigRule has higher order than networkCallbackRule, so
450             // networkCallbackRule is the outer rule and will be cleaned up after this method.
451             final TestableNetworkCallback callback =
452                     networkCallbackRule.registerDefaultNetworkCallback();
453             assertNotNull("Couldn't restore Internet connectivity",
454                     callback.eventuallyExpect(CallbackEntry.AVAILABLE));
455         });
456     }
457 
458     @Test
testIsNetworkTypeValid()459     public void testIsNetworkTypeValid() {
460         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE));
461         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_WIFI));
462         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_MMS));
463         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_SUPL));
464         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_DUN));
465         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_HIPRI));
466         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_WIMAX));
467         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_BLUETOOTH));
468         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_DUMMY));
469         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_ETHERNET));
470         assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_FOTA));
471         assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_IMS));
472         assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_CBS));
473         assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_WIFI_P2P));
474         assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_IA));
475         assertFalse(mCm.isNetworkTypeValid(-1));
476         assertTrue(mCm.isNetworkTypeValid(0));
477         assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.MAX_NETWORK_TYPE));
478         assertFalse(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.MAX_NETWORK_TYPE+1));
479 
480         NetworkInfo[] ni = mCm.getAllNetworkInfo();
481 
482         for (NetworkInfo n: ni) {
483             assertTrue(ConnectivityManager.isNetworkTypeValid(n.getType()));
484         }
485 
486     }
487 
488     @Test
testSetNetworkPreference()489     public void testSetNetworkPreference() {
490         // getNetworkPreference() and setNetworkPreference() are both deprecated so they do
491         // not preform any action.  Verify they are at least still callable.
492         mCm.setNetworkPreference(mCm.getNetworkPreference());
493     }
494 
495     @Test
testGetActiveNetworkInfo()496     public void testGetActiveNetworkInfo() {
497         NetworkInfo ni = mCm.getActiveNetworkInfo();
498 
499         assertNotNull("You must have an active network connection to complete CTS", ni);
500         assertTrue(ConnectivityManager.isNetworkTypeValid(ni.getType()));
501         assertTrue(ni.getState() == State.CONNECTED);
502     }
503 
504     @Test
testGetActiveNetwork()505     public void testGetActiveNetwork() {
506         Network network = mCm.getActiveNetwork();
507         assertNotNull("You must have an active network connection to complete CTS", network);
508 
509         NetworkInfo ni = mCm.getNetworkInfo(network);
510         assertNotNull("Network returned from getActiveNetwork was invalid", ni);
511 
512         // Similar to testGetActiveNetworkInfo above.
513         assertTrue(ConnectivityManager.isNetworkTypeValid(ni.getType()));
514         assertTrue(ni.getState() == State.CONNECTED);
515     }
516 
517     @Test
testGetNetworkInfo()518     public void testGetNetworkInfo() {
519         for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE+1; type++) {
520             if (shouldBeSupported(type)) {
521                 NetworkInfo ni = mCm.getNetworkInfo(type);
522                 assertTrue("Info shouldn't be null for " + type, ni != null);
523                 State state = ni.getState();
524                 assertTrue("Bad state for " + type, State.UNKNOWN.ordinal() >= state.ordinal()
525                            && state.ordinal() >= State.CONNECTING.ordinal());
526                 DetailedState ds = ni.getDetailedState();
527                 assertTrue("Bad detailed state for " + type,
528                            DetailedState.FAILED.ordinal() >= ds.ordinal()
529                            && ds.ordinal() >= DetailedState.IDLE.ordinal());
530             } else {
531                 assertNull("Info should be null for " + type, mCm.getNetworkInfo(type));
532             }
533         }
534     }
535 
536     @Test
testGetAllNetworkInfo()537     public void testGetAllNetworkInfo() {
538         NetworkInfo[] ni = mCm.getAllNetworkInfo();
539         assertTrue(ni.length >= MIN_NUM_NETWORK_TYPES);
540         for (int type = 0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
541             int desiredFoundCount = (shouldBeSupported(type) ? 1 : 0);
542             int foundCount = 0;
543             for (NetworkInfo i : ni) {
544                 if (i.getType() == type) foundCount++;
545             }
546             if (foundCount != desiredFoundCount) {
547                 Log.e(TAG, "failure in testGetAllNetworkInfo.  Dump of returned NetworkInfos:");
548                 for (NetworkInfo networkInfo : ni) Log.e(TAG, "  " + networkInfo);
549             }
550             assertTrue("Unexpected foundCount of " + foundCount + " for type " + type,
551                     foundCount == desiredFoundCount);
552         }
553     }
554 
getSubscriberIdForCellNetwork(Network cellNetwork)555     private String getSubscriberIdForCellNetwork(Network cellNetwork) {
556         final NetworkCapabilities cellCaps = mCm.getNetworkCapabilities(cellNetwork);
557         final NetworkSpecifier specifier = cellCaps.getNetworkSpecifier();
558         assertTrue(specifier instanceof TelephonyNetworkSpecifier);
559         // Get subscription from Telephony network specifier.
560         final int subId = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
561         assertNotEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID, subId);
562 
563         // Get subscriber Id from telephony manager.
564         final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
565         return runWithShellPermissionIdentity(() -> tm.getSubscriberId(subId),
566                 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
567     }
568 
569     @AppModeFull(reason = "Cannot request network in instant app mode")
570     @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
571     @Test
testGetAllNetworkStateSnapshots()572     public void testGetAllNetworkStateSnapshots()
573             throws InterruptedException {
574         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
575         // Make sure cell is active to retrieve IMSI for verification in later step.
576         final Network cellNetwork = networkCallbackRule.requestCell();
577         final String subscriberId = getSubscriberIdForCellNetwork(cellNetwork);
578         assertFalse(TextUtils.isEmpty(subscriberId));
579 
580         // Verify the API cannot be called without proper permission.
581         assertThrows(SecurityException.class, () -> mCm.getAllNetworkStateSnapshots());
582 
583         // Get all networks, verify the result of getAllNetworkStateSnapshots matches the result
584         // got from other APIs.
585         final Network[] networks = mCm.getAllNetworks();
586         assertGreaterOrEqual(networks.length, 1);
587         final TestableNetworkCallback allNetworkLinkPropertiesListener =
588                 new TestableNetworkCallback();
589         mCm.registerNetworkCallback(new NetworkRequest.Builder().clearCapabilities().build(),
590                 allNetworkLinkPropertiesListener);
591 
592         final List<NetworkStateSnapshot> snapshots = runWithShellPermissionIdentity(
593                 () -> mCm.getAllNetworkStateSnapshots(), NETWORK_SETTINGS);
594         assertEquals(networks.length, snapshots.size());
595         for (final Network network : networks) {
596             // Can't use a lambda because it will cause the test to crash on R with
597             // NoClassDefFoundError.
598             NetworkStateSnapshot snapshot = null;
599             for (NetworkStateSnapshot item : snapshots) {
600                 if (item.getNetwork().equals(network)) {
601                     snapshot = item;
602                     break;
603                 }
604             }
605             assertNotNull(snapshot);
606             final NetworkCapabilities caps =
607                     Objects.requireNonNull(mCm.getNetworkCapabilities(network));
608             // Redact specifier of the capabilities of the snapshot before comparing since
609             // the result returned from getNetworkCapabilities always get redacted.
610             final NetworkSpecifier snapshotCapSpecifier =
611                     snapshot.getNetworkCapabilities().getNetworkSpecifier();
612             final NetworkSpecifier redactedSnapshotCapSpecifier =
613                     snapshotCapSpecifier == null ? null : snapshotCapSpecifier.redact();
614             assertEquals("", caps.describeImmutableDifferences(
615                     snapshot.getNetworkCapabilities()
616                             .setNetworkSpecifier(redactedSnapshotCapSpecifier)));
617 
618             // Don't check that the mutable fields are the same with synchronous calls, as
619             // the device may add or remove content of these fields in the middle of the test.
620             // Instead, search the target LinkProperties from received LinkPropertiesChanged
621             // callbacks. This is guaranteed to succeed because the callback is registered
622             // before getAllNetworkStateSnapshots is called.
623             final LinkProperties lpFromSnapshot = snapshot.getLinkProperties();
624             allNetworkLinkPropertiesListener.eventuallyExpect(CallbackEntry.LINK_PROPERTIES_CHANGED,
625                     NETWORK_CALLBACK_TIMEOUT_MS, 0 /* mark */, entry ->
626                             entry.getNetwork().equals(network)
627                                     && entry.getLp().equals(lpFromSnapshot));
628 
629             assertEquals(mCm.getNetworkInfo(network).getType(), snapshot.getLegacyType());
630 
631             if (network.equals(cellNetwork)) {
632                 assertEquals(subscriberId, snapshot.getSubscriberId());
633             }
634         }
635     }
636 
checkPermission(String perm, int uid)637     private boolean checkPermission(String perm, int uid) {
638         return mContext.checkPermission(perm, -1 /* pid */, uid) == PERMISSION_GRANTED;
639     }
640 
findPackageByPermissions(@onNull List<String> requiredPermissions, @NonNull List<String> forbiddenPermissions)641     private String findPackageByPermissions(@NonNull List<String> requiredPermissions,
642                 @NonNull List<String> forbiddenPermissions) throws Exception {
643         final List<PackageInfo> packageInfos =
644                 mPackageManager.getInstalledPackages(GET_PERMISSIONS);
645         for (PackageInfo packageInfo : packageInfos) {
646             final int uid = mPackageManager.getPackageUid(packageInfo.packageName, 0 /* flags */);
647             if (!CollectionUtils.all(requiredPermissions, perm -> checkPermission(perm, uid))) {
648                 continue;
649             }
650             if (CollectionUtils.any(forbiddenPermissions, perm -> checkPermission(perm, uid))) {
651                 continue;
652             }
653 
654             return packageInfo.packageName;
655         }
656         return null;
657     }
658 
659     @DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
660     @AppModeFull(reason = "Cannot get installed packages in instant app mode")
661     @Test
testGetRedactedLinkPropertiesForPackage()662     public void testGetRedactedLinkPropertiesForPackage() throws Exception {
663         final String groundedPkg = findPackageByPermissions(
664                 List.of(), /* requiredPermissions */
665                 List.of(ACCESS_NETWORK_STATE) /* forbiddenPermissions */);
666         assertNotNull("Couldn't find any package without ACCESS_NETWORK_STATE", groundedPkg);
667         final int groundedUid = mPackageManager.getPackageUid(groundedPkg, 0 /* flags */);
668 
669         final String normalPkg = findPackageByPermissions(
670                 List.of(ACCESS_NETWORK_STATE) /* requiredPermissions */,
671                 List.of(NETWORK_SETTINGS, NETWORK_STACK,
672                         PERMISSION_MAINLINE_NETWORK_STACK) /* forbiddenPermissions */);
673         assertNotNull("Couldn't find any package with ACCESS_NETWORK_STATE but"
674                 + " without NETWORK_SETTINGS", normalPkg);
675         final int normalUid = mPackageManager.getPackageUid(normalPkg, 0 /* flags */);
676 
677         // There are some privileged packages on the system, like the phone process, the network
678         // stack and the system server.
679         final String privilegedPkg = findPackageByPermissions(
680                 List.of(ACCESS_NETWORK_STATE, NETWORK_SETTINGS), /* requiredPermissions */
681                 List.of() /* forbiddenPermissions */);
682         assertNotNull("Couldn't find a package with sufficient permissions", privilegedPkg);
683         final int privilegedUid = mPackageManager.getPackageUid(privilegedPkg, 0);
684 
685         // Set parcelSensitiveFields to true to preserve CaptivePortalApiUrl & CaptivePortalData
686         // when parceling.
687         final LinkProperties lp = new LinkProperties(new LinkProperties(),
688                 true /* parcelSensitiveFields */);
689         final Uri capportUrl = Uri.parse("https://capport.example.com/api");
690         final CaptivePortalData capportData = new CaptivePortalData.Builder().build();
691         final int mtu = 12345;
692         lp.setMtu(mtu);
693         lp.setCaptivePortalApiUrl(capportUrl);
694         lp.setCaptivePortalData(capportData);
695 
696         // No matter what the given uid is, a SecurityException will be thrown if the caller
697         // doesn't hold the NETWORK_SETTINGS permission.
698         assertThrows(SecurityException.class,
699                 () -> mCm.getRedactedLinkPropertiesForPackage(lp, groundedUid, groundedPkg));
700         assertThrows(SecurityException.class,
701                 () -> mCm.getRedactedLinkPropertiesForPackage(lp, normalUid, normalPkg));
702         assertThrows(SecurityException.class,
703                 () -> mCm.getRedactedLinkPropertiesForPackage(lp, privilegedUid, privilegedPkg));
704 
705         runAsShell(NETWORK_SETTINGS, () -> {
706             // No matter what the given uid is, if the given LinkProperties is null, then
707             // NullPointerException will be thrown.
708             assertThrows(NullPointerException.class,
709                     () -> mCm.getRedactedLinkPropertiesForPackage(null, groundedUid, groundedPkg));
710             assertThrows(NullPointerException.class,
711                     () -> mCm.getRedactedLinkPropertiesForPackage(null, normalUid, normalPkg));
712             assertThrows(NullPointerException.class,
713                     () -> mCm.getRedactedLinkPropertiesForPackage(
714                             null, privilegedUid, privilegedPkg));
715 
716             // Make sure null is returned for a UID without ACCESS_NETWORK_STATE.
717             assertNull(mCm.getRedactedLinkPropertiesForPackage(lp, groundedUid, groundedPkg));
718 
719             // CaptivePortalApiUrl & CaptivePortalData will be set to null if given uid doesn't hold
720             // the NETWORK_SETTINGS permission.
721             assertNull(mCm.getRedactedLinkPropertiesForPackage(lp, normalUid, normalPkg)
722                     .getCaptivePortalApiUrl());
723             assertNull(mCm.getRedactedLinkPropertiesForPackage(lp, normalUid, normalPkg)
724                     .getCaptivePortalData());
725             // MTU is not sensitive and is not redacted.
726             assertEquals(mtu, mCm.getRedactedLinkPropertiesForPackage(lp, normalUid, normalPkg)
727                     .getMtu());
728 
729             // CaptivePortalApiUrl & CaptivePortalData will be preserved if the given uid holds the
730             // NETWORK_SETTINGS permission.
731             assertNotNull(lp.getCaptivePortalApiUrl());
732             assertNotNull(lp.getCaptivePortalData());
733             assertEquals(lp.getCaptivePortalApiUrl(),
734                     mCm.getRedactedLinkPropertiesForPackage(lp, privilegedUid, privilegedPkg)
735                             .getCaptivePortalApiUrl());
736             assertEquals(lp.getCaptivePortalData(),
737                     mCm.getRedactedLinkPropertiesForPackage(lp, privilegedUid, privilegedPkg)
738                             .getCaptivePortalData());
739         });
740     }
741 
redactNc(@onNull final NetworkCapabilities nc, int uid, @NonNull String packageName)742     private NetworkCapabilities redactNc(@NonNull final NetworkCapabilities nc, int uid,
743             @NonNull String packageName) {
744         return mCm.getRedactedNetworkCapabilitiesForPackage(nc, uid, packageName);
745     }
746 
747     @ConnectivityModuleTest
748     @DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
749     @AppModeFull(reason = "Cannot get installed packages in instant app mode")
750     @Test
testGetRedactedNetworkCapabilitiesForPackage()751     public void testGetRedactedNetworkCapabilitiesForPackage() throws Exception {
752         final String groundedPkg = findPackageByPermissions(
753                 List.of(), /* requiredPermissions */
754                 List.of(ACCESS_NETWORK_STATE) /* forbiddenPermissions */);
755         assertNotNull("Couldn't find any package without ACCESS_NETWORK_STATE", groundedPkg);
756         final int groundedUid = mPackageManager.getPackageUid(groundedPkg, 0 /* flags */);
757 
758         // A package which doesn't have any of the permissions below, but has NETWORK_STATE.
759         // There should be a number of packages like this on the device; AOSP has many,
760         // including contacts, webview, the keyboard, pacprocessor, messaging.
761         final String normalPkg = findPackageByPermissions(
762                 List.of(ACCESS_NETWORK_STATE) /* requiredPermissions */,
763                 List.of(NETWORK_SETTINGS, NETWORK_FACTORY, NETWORK_SETUP_WIZARD,
764                         NETWORK_STACK, PERMISSION_MAINLINE_NETWORK_STACK,
765                         ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION) /* forbiddenPermissions */);
766         assertNotNull("Can't find a package with ACCESS_NETWORK_STATE but without any of"
767                 + " the forbidden permissions", normalPkg);
768         final int normalUid = mPackageManager.getPackageUid(normalPkg, 0 /* flags */);
769 
770         // There are some privileged packages on the system, like the phone process, the network
771         // stack and the system server.
772         final String privilegedPkg = findPackageByPermissions(
773                 List.of(ACCESS_NETWORK_STATE, NETWORK_SETTINGS, NETWORK_FACTORY,
774                         ACCESS_FINE_LOCATION), /* requiredPermissions */
775                 List.of() /* forbiddenPermissions */);
776         assertNotNull("Couldn't find a package with sufficient permissions", privilegedPkg);
777         final int privilegedUid = mPackageManager.getPackageUid(privilegedPkg, 0);
778 
779         final Set<Range<Integer>> uids = new ArraySet<>();
780         uids.add(new Range<>(10000, 10100));
781         uids.add(new Range<>(10200, 10300));
782         final String ssid = "My-WiFi";
783         // This test will set underlying networks in the capabilities to redact to see if they
784         // are appropriately redacted, so fetch the default network to put in there as an example.
785         final Network defaultNetwork = mCm.getActiveNetwork();
786         assertNotNull("CTS requires a working Internet connection", defaultNetwork);
787         final int subId1 = 1;
788         final int subId2 = 2;
789         final int[] administratorUids = {normalUid};
790         final String bssid = "location sensitive";
791         final int rssi = 43; // not location sensitive
792         final WifiInfo wifiInfo = new WifiInfo.Builder()
793                 .setBssid(bssid)
794                 .setRssi(rssi)
795                 .build();
796         final NetworkCapabilities nc = new NetworkCapabilities.Builder()
797                 .setUids(uids)
798                 .setSsid(ssid)
799                 .setUnderlyingNetworks(List.of(defaultNetwork))
800                 .setSubscriptionIds(Set.of(subId1, subId2))
801                 .setAdministratorUids(administratorUids)
802                 .setOwnerUid(normalUid)
803                 .setTransportInfo(wifiInfo)
804                 .build();
805 
806         // No matter what the given uid is, a SecurityException will be thrown if the caller
807         // doesn't hold the NETWORK_SETTINGS permission.
808         assertThrows(SecurityException.class, () -> redactNc(nc, groundedUid, groundedPkg));
809         assertThrows(SecurityException.class, () -> redactNc(nc, normalUid, normalPkg));
810         assertThrows(SecurityException.class, () -> redactNc(nc, privilegedUid, privilegedPkg));
811 
812         runAsShell(NETWORK_SETTINGS, () -> {
813             // Make sure that the NC is null if the package doesn't hold ACCESS_NETWORK_STATE.
814             assertNull(redactNc(nc, groundedUid, groundedPkg));
815 
816             // Uids, ssid & underlying networks will be redacted if the given uid
817             // doesn't hold the associated permissions. The wifi transport info is also suitably
818             // redacted.
819             final NetworkCapabilities redactedNormal = redactNc(nc, normalUid, normalPkg);
820             assertNull(redactedNormal.getUids());
821             assertNull(redactedNormal.getSsid());
822             assertNull(redactedNormal.getUnderlyingNetworks());
823             // TODO: Make subIds public and update to verify the size is 2
824             final int subIdsSize = redactedNormal.getSubscriptionIds().size();
825             assertTrue(subIdsSize == 0 || subIdsSize == 2);
826             assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS,
827                     ((WifiInfo) redactedNormal.getTransportInfo()).getBSSID());
828             assertEquals(rssi, ((WifiInfo) redactedNormal.getTransportInfo()).getRssi());
829 
830             // Uids, ssid, underlying networks & subscriptionIds will be preserved if the given uid
831             // holds the associated permissions.
832             final NetworkCapabilities redactedPrivileged =
833                     redactNc(nc, privilegedUid, privilegedPkg);
834             assertEquals(uids, redactedPrivileged.getUids());
835             assertEquals(ssid, redactedPrivileged.getSsid());
836             assertEquals(List.of(defaultNetwork), redactedPrivileged.getUnderlyingNetworks());
837             assertEquals(Set.of(subId1, subId2), redactedPrivileged.getSubscriptionIds());
838             assertEquals(bssid, ((WifiInfo) redactedPrivileged.getTransportInfo()).getBSSID());
839             assertEquals(rssi, ((WifiInfo) redactedPrivileged.getTransportInfo()).getRssi());
840 
841             // The owner uid is only preserved when the network is a VPN and the uid is the
842             // same as the owner uid.
843             nc.addTransportType(TRANSPORT_VPN);
844             assertEquals(normalUid, redactNc(nc, normalUid, normalPkg).getOwnerUid());
845             assertEquals(INVALID_UID, redactNc(nc, privilegedUid, privilegedPkg).getOwnerUid());
846             nc.removeTransportType(TRANSPORT_VPN);
847 
848             // If the given uid doesn't hold location permissions, the owner uid will be set to
849             // INVALID_UID even when sent to that UID (this avoids a wifi suggestor knowing where
850             // the device is by virtue of the device connecting to its own network).
851             assertEquals(INVALID_UID, redactNc(nc, normalUid, normalPkg).getOwnerUid());
852 
853             // If the given uid holds location permissions, the owner uid is preserved. This works
854             // because the shell holds ACCESS_FINE_LOCATION.
855             final int[] administratorUids2 = { privilegedUid };
856             nc.setAdministratorUids(administratorUids2);
857             nc.setOwnerUid(privilegedUid);
858             assertEquals(privilegedUid, redactNc(nc, privilegedUid, privilegedPkg).getOwnerUid());
859         });
860     }
861 
862     /**
863      * Tests that connections can be opened on WiFi and cellphone networks,
864      * and that they are made from different IP addresses.
865      */
866     @AppModeFull(reason = "Cannot get WifiManager or access the SD card in instant app mode")
867     @Test
868     @RequiresDevice // Virtual devices use a single internet connection for all networks
testOpenConnection()869     public void testOpenConnection() throws Exception {
870         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
871         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
872 
873         Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
874         Network cellNetwork = networkCallbackRule.requestCell();
875         // This server returns the requestor's IP address as the response body.
876         String ipAddressEchoUrl = getIpAddressEchoUrlFromConfig();
877         URL url = new URL(ipAddressEchoUrl);
878         String wifiAddressString = httpGet(wifiNetwork, url);
879         String cellAddressString = httpGet(cellNetwork, url);
880 
881         assertFalse(String.format("Same address '%s' on two different networks (%s, %s)",
882                 wifiAddressString, wifiNetwork, cellNetwork),
883                 wifiAddressString.equals(cellAddressString));
884 
885         // Verify that the IP addresses that the requests appeared to come from are actually on the
886         // respective networks.
887         assertOnNetwork(wifiAddressString, wifiNetwork);
888         assertOnNetwork(cellAddressString, cellNetwork);
889 
890         assertFalse("Unexpectedly equal: " + wifiNetwork, wifiNetwork.equals(cellNetwork));
891     }
892 
893     /**
894      * Gets IP address echo url from dynamic config.
895      */
getIpAddressEchoUrlFromConfig()896     private static String getIpAddressEchoUrlFromConfig() throws Exception {
897         Bundle instrumentationArgs = InstrumentationRegistry.getArguments();
898         String testModuleName = instrumentationArgs.getString(TEST_MODULE_NAME_OPTION);
899         // Get the DynamicConfig.xml contents and extract the ipv6 test URL.
900         DynamicConfigDeviceSide dynamicConfig = new DynamicConfigDeviceSide(testModuleName);
901         String ipAddressEchoUrl = dynamicConfig.getValue(IP_ADDRESS_ECHO_URL_KEY);
902         assertContains(ALLOWED_IP_ADDRESS_ECHO_URLS, ipAddressEchoUrl);
903         return ipAddressEchoUrl;
904     }
905 
906     /**
907      * Performs a HTTP GET to the specified URL on the specified Network, and returns
908      * the response body decoded as UTF-8.
909      */
httpGet(Network network, URL httpUrl)910     private static String httpGet(Network network, URL httpUrl) throws IOException {
911         HttpURLConnection connection = (HttpURLConnection) network.openConnection(httpUrl);
912         try {
913             InputStream inputStream = connection.getInputStream();
914             return Streams.readFully(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
915         } finally {
916             connection.disconnect();
917         }
918     }
919 
assertOnNetwork(String adressString, Network network)920     private void assertOnNetwork(String adressString, Network network) throws UnknownHostException {
921         InetAddress address = InetAddress.getByName(adressString);
922         LinkProperties linkProperties = mCm.getLinkProperties(network);
923         // To make sure that the request went out on the right network, check that
924         // the IP address seen by the server is assigned to the expected network.
925         // We can only do this for IPv6 addresses, because in IPv4 we will likely
926         // have a private IPv4 address, and that won't match what the server sees.
927         if (address instanceof Inet6Address) {
928             assertContains(linkProperties.getAddresses(), address);
929         }
930     }
931 
assertContains(Collection<T> collection, T element)932     private static<T> void assertContains(Collection<T> collection, T element) {
933         assertTrue(element + " not found in " + collection, collection.contains(element));
934     }
935 
assertStartUsingNetworkFeatureUnsupported(int networkType, String feature)936     private void assertStartUsingNetworkFeatureUnsupported(int networkType, String feature) {
937         try {
938             mCm.startUsingNetworkFeature(networkType, feature);
939             fail("startUsingNetworkFeature is no longer supported in the current API version");
940         } catch (UnsupportedOperationException expected) {}
941     }
942 
assertStopUsingNetworkFeatureUnsupported(int networkType, String feature)943     private void assertStopUsingNetworkFeatureUnsupported(int networkType, String feature) {
944         try {
945             mCm.startUsingNetworkFeature(networkType, feature);
946             fail("stopUsingNetworkFeature is no longer supported in the current API version");
947         } catch (UnsupportedOperationException expected) {}
948     }
949 
assertRequestRouteToHostUnsupported(int networkType, int hostAddress)950     private void assertRequestRouteToHostUnsupported(int networkType, int hostAddress) {
951         try {
952             mCm.requestRouteToHost(networkType, hostAddress);
953             fail("requestRouteToHost is no longer supported in the current API version");
954         } catch (UnsupportedOperationException expected) {}
955     }
956 
957     @Test
testStartUsingNetworkFeature()958     public void testStartUsingNetworkFeature() {
959 
960         final String invalidateFeature = "invalidateFeature";
961         final String mmsFeature = "enableMMS";
962 
963         assertStartUsingNetworkFeatureUnsupported(TYPE_MOBILE, invalidateFeature);
964         assertStopUsingNetworkFeatureUnsupported(TYPE_MOBILE, invalidateFeature);
965         assertStartUsingNetworkFeatureUnsupported(TYPE_WIFI, mmsFeature);
966     }
967 
shouldEthernetBeSupported()968     private boolean shouldEthernetBeSupported() {
969         // Instant mode apps aren't allowed to query the Ethernet service due to selinux policies.
970         // When in instant mode, don't fail if the Ethernet service is available. Instead, rely on
971         // the fact that Ethernet should be supported if the device has a hardware Ethernet port, or
972         // if the device can be a USB host and thus can use USB Ethernet adapters.
973         //
974         // Note that this test this will still fail in instant mode if a device supports Ethernet
975         // via other hardware means. We are not currently aware of any such device.
976         return hasEthernetService()
977                 || mPackageManager.hasSystemFeature(FEATURE_ETHERNET)
978                 || mPackageManager.hasSystemFeature(FEATURE_USB_HOST);
979     }
980 
hasEthernetService()981     private boolean hasEthernetService() {
982         // On Q creating EthernetManager from a thread that does not have a looper (like the test
983         // thread) crashes because it tried to use Looper.myLooper() through the default Handler
984         // constructor to run onAvailabilityChanged callbacks. Use ServiceManager to check whether
985         // the service exists instead.
986         // TODO: remove once Q is no longer supported in MTS, as ServiceManager is hidden API
987         if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
988             return ServiceManager.getService(Context.ETHERNET_SERVICE) != null;
989         }
990         return mContext.getSystemService(Context.ETHERNET_SERVICE) != null;
991     }
992 
shouldBeSupported(int networkType)993     private boolean shouldBeSupported(int networkType) {
994         return mNetworkTypes.contains(networkType)
995             || (networkType == ConnectivityManager.TYPE_VPN)
996             || (networkType == ConnectivityManager.TYPE_ETHERNET && shouldEthernetBeSupported());
997     }
998 
999     @Test
testIsNetworkSupported()1000     public void testIsNetworkSupported() {
1001         for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
1002             boolean supported = mCm.isNetworkSupported(type);
1003             if (shouldBeSupported(type)) {
1004                 assertTrue("Network type " + type + " should be supported", supported);
1005             } else {
1006                 assertFalse("Network type " + type + " should not be supported", supported);
1007             }
1008         }
1009     }
1010 
1011     @Test
testRequestRouteToHost()1012     public void testRequestRouteToHost() {
1013         for (int type = -1 ; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
1014             assertRequestRouteToHostUnsupported(type, HOST_ADDRESS);
1015         }
1016     }
1017 
1018     @Test
testTest()1019     public void testTest() {
1020         mCm.getBackgroundDataSetting();
1021     }
1022 
makeDefaultRequest()1023     private NetworkRequest makeDefaultRequest() {
1024         // Make a request that is similar to the way framework tracks the system
1025         // default network.
1026         return new NetworkRequest.Builder()
1027                 .clearCapabilities()
1028                 .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
1029                 .addCapability(NET_CAPABILITY_TRUSTED)
1030                 .addCapability(NET_CAPABILITY_NOT_VPN)
1031                 .addCapability(NET_CAPABILITY_INTERNET)
1032                 .build();
1033     }
1034 
makeWifiNetworkRequest()1035     private NetworkRequest makeWifiNetworkRequest() {
1036         return new NetworkRequest.Builder()
1037                 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
1038                 .addCapability(NET_CAPABILITY_INTERNET)
1039                 .build();
1040     }
1041 
makeCellNetworkRequest()1042     private NetworkRequest makeCellNetworkRequest() {
1043         return new NetworkRequest.Builder()
1044                 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
1045                 .addCapability(NET_CAPABILITY_INTERNET)
1046                 .build();
1047     }
1048 
hasPrivateDnsValidated(CallbackEntry entry, Network networkForPrivateDns)1049     private boolean hasPrivateDnsValidated(CallbackEntry entry, Network networkForPrivateDns) {
1050         if (!networkForPrivateDns.equals(entry.getNetwork())) return false;
1051         final NetworkCapabilities nc = ((CallbackEntry.CapabilitiesChanged) entry).getCaps();
1052         return !nc.isPrivateDnsBroken() && nc.hasCapability(NET_CAPABILITY_VALIDATED);
1053     }
1054 
1055     @AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
1056     @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
1057     @SkipMainlinePresubmit(reason = "Out of SLO flakiness")
testIsPrivateDnsBroken()1058     public void testIsPrivateDnsBroken() throws InterruptedException {
1059         final String invalidPrivateDnsServer = "invalidhostname.example.com";
1060         final String goodPrivateDnsServer = "dns.google";
1061         mCtsNetUtils.storePrivateDnsSetting();
1062         final NetworkRequest networkRequest = new NetworkRequest.Builder()
1063                 .addCapability(NET_CAPABILITY_INTERNET).build();
1064         final TestableNetworkCallback cb =
1065                 networkCallbackRule.registerNetworkCallback(networkRequest);
1066         final Network networkForPrivateDns = mCm.getActiveNetwork();
1067         try {
1068             // Verifying the good private DNS sever
1069             mCtsNetUtils.setPrivateDnsStrictMode(goodPrivateDnsServer);
1070             cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, NETWORK_CALLBACK_TIMEOUT_MS,
1071                     entry -> hasPrivateDnsValidated(entry, networkForPrivateDns));
1072 
1073             // Verifying the broken private DNS sever
1074             mCtsNetUtils.setPrivateDnsStrictMode(invalidPrivateDnsServer);
1075             cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, NETWORK_CALLBACK_TIMEOUT_MS,
1076                     entry -> (((CallbackEntry.CapabilitiesChanged) entry).getCaps()
1077                     .isPrivateDnsBroken()) && networkForPrivateDns.equals(entry.getNetwork()));
1078         } finally {
1079             mCtsNetUtils.restorePrivateDnsSetting();
1080             // Toggle network to make sure it is re-validated
1081             mCm.reportNetworkConnectivity(networkForPrivateDns, true);
1082             cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, NETWORK_CALLBACK_TIMEOUT_MS,
1083                     entry -> !(((CallbackEntry.CapabilitiesChanged) entry).getCaps()
1084                     .isPrivateDnsBroken()) && networkForPrivateDns.equals(entry.getNetwork()));
1085         }
1086     }
1087 
1088     /**
1089      * Exercises both registerNetworkCallback and unregisterNetworkCallback. This checks to
1090      * see if we get a callback for the TRANSPORT_WIFI transport type being available.
1091      *
1092      * <p>In order to test that a NetworkCallback occurs, we need some change in the network
1093      * state (either a transport or capability is now available). The most straightforward is
1094      * WiFi. We could add a version that uses the telephony data connection but it's not clear
1095      * that it would increase test coverage by much (how many devices have 3G radio but not Wifi?).
1096      */
1097     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1098     @Test
testRegisterNetworkCallback()1099     public void testRegisterNetworkCallback() throws Exception {
1100         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
1101 
1102         // We will register for a WIFI network being available or lost.
1103         final TestableNetworkCallback callback = networkCallbackRule.registerNetworkCallback(
1104                 makeWifiNetworkRequest());
1105 
1106         final TestableNetworkCallback defaultTrackingCallback =
1107                 networkCallbackRule.registerDefaultNetworkCallback();
1108 
1109         final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback();
1110         final TestableNetworkCallback perUidCallback = new TestableNetworkCallback();
1111         final TestableNetworkCallback bestMatchingCallback = new TestableNetworkCallback();
1112         final Handler h = new Handler(Looper.getMainLooper());
1113         if (TestUtils.shouldTestSApis()) {
1114             assertThrows(SecurityException.class, () ->
1115                     networkCallbackRule.registerSystemDefaultNetworkCallback(
1116                             systemDefaultCallback, h));
1117             runWithShellPermissionIdentity(() -> {
1118                 networkCallbackRule.registerSystemDefaultNetworkCallback(systemDefaultCallback, h);
1119                 networkCallbackRule.registerDefaultNetworkCallbackForUid(Process.myUid(),
1120                         perUidCallback, h);
1121             }, NETWORK_SETTINGS);
1122             networkCallbackRule.registerBestMatchingNetworkCallback(
1123                     makeDefaultRequest(), bestMatchingCallback, h);
1124         }
1125 
1126         Network wifiNetwork = null;
1127         mCtsNetUtils.ensureWifiConnected();
1128 
1129         // Now we should expect to get a network callback about availability of the wifi
1130         // network even if it was already connected as a state-based action when the callback
1131         // is registered.
1132         wifiNetwork = callback.eventuallyExpect(CallbackEntry.AVAILABLE).getNetwork();
1133         assertNotNull("Did not receive onAvailable for TRANSPORT_WIFI request",
1134                 wifiNetwork);
1135 
1136         final Network defaultNetwork = defaultTrackingCallback.eventuallyExpect(
1137                 CallbackEntry.AVAILABLE).getNetwork();
1138         assertNotNull("Did not receive onAvailable on default network callback",
1139                 defaultNetwork);
1140 
1141         if (TestUtils.shouldTestSApis()) {
1142             systemDefaultCallback.eventuallyExpect(CallbackEntry.AVAILABLE);
1143             final Network perUidNetwork = perUidCallback.eventuallyExpect(CallbackEntry.AVAILABLE)
1144                     .getNetwork();
1145             assertEquals(defaultNetwork, perUidNetwork);
1146             final Network bestMatchingNetwork = bestMatchingCallback.eventuallyExpect(
1147                     CallbackEntry.AVAILABLE).getNetwork();
1148             assertEquals(defaultNetwork, bestMatchingNetwork);
1149         }
1150     }
1151 
1152     @ConnectivityModuleTest
1153     @IgnoreUpTo(Build.VERSION_CODES.R)
1154     @Test
testRegisterSystemDefaultNetworkCallbackPermission()1155     public void testRegisterSystemDefaultNetworkCallbackPermission() {
1156         final Handler h = new Handler(Looper.getMainLooper());
1157         // Verify registerSystemDefaultNetworkCallback can be accessed via
1158         // CONNECTIVITY_USE_RESTRICTED_NETWORKS permission.
1159         runWithShellPermissionIdentity(
1160                 () -> networkCallbackRule.registerSystemDefaultNetworkCallback(h),
1161                 CONNECTIVITY_USE_RESTRICTED_NETWORKS);
1162     }
1163 
1164     /**
1165      * Tests both registerNetworkCallback and unregisterNetworkCallback similarly to
1166      * {@link #testRegisterNetworkCallback} except that a {@code PendingIntent} is used instead
1167      * of a {@code NetworkCallback}.
1168      */
1169     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1170     @Test
testRegisterNetworkCallback_withPendingIntent()1171     public void testRegisterNetworkCallback_withPendingIntent() {
1172         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
1173 
1174         // Create a ConnectivityActionReceiver that has an IntentFilter for our locally defined
1175         // action, NETWORK_CALLBACK_ACTION.
1176         final IntentFilter filter = new IntentFilter();
1177         filter.addAction(NETWORK_CALLBACK_ACTION);
1178 
1179         final ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
1180                 mCm, ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED);
1181         final int flags = SdkLevel.isAtLeastT() ? RECEIVER_EXPORTED : 0;
1182         mContext.registerReceiver(receiver, filter, flags);
1183 
1184         // Create a broadcast PendingIntent for NETWORK_CALLBACK_ACTION.
1185         final Intent intent = new Intent(NETWORK_CALLBACK_ACTION)
1186                 .setPackage(mContext.getPackageName());
1187         // While ConnectivityService would put extra info such as network or request id before
1188         // broadcasting the inner intent. The MUTABLE flag needs to be added accordingly.
1189         final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0 /*requestCode*/,
1190                 intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE);
1191 
1192         // We will register for a WIFI network being available or lost.
1193         mCm.registerNetworkCallback(makeWifiNetworkRequest(), pendingIntent);
1194 
1195         try {
1196             mCtsNetUtils.ensureWifiConnected();
1197 
1198             // Now we expect to get the Intent delivered notifying of the availability of the wifi
1199             // network even if it was already connected as a state-based action when the callback
1200             // is registered.
1201             assertTrue("Did not receive expected Intent " + intent + " for TRANSPORT_WIFI",
1202                     receiver.waitForState());
1203         } catch (InterruptedException e) {
1204             fail("Broadcast receiver or NetworkCallback wait was interrupted.");
1205         } finally {
1206             mCm.unregisterNetworkCallback(pendingIntent);
1207             pendingIntent.cancel();
1208             mContext.unregisterReceiver(receiver);
1209         }
1210     }
1211 
runIdenticalPendingIntentsRequestTest(boolean useListen)1212     private void runIdenticalPendingIntentsRequestTest(boolean useListen) throws Exception {
1213         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
1214 
1215         // Disconnect before registering callbacks, reconnect later to fire them
1216         mCtsNetUtils.ensureWifiDisconnected(null);
1217 
1218         final NetworkRequest firstRequest = makeWifiNetworkRequest();
1219         final NetworkRequest secondRequest = new NetworkRequest(firstRequest);
1220         // Will match wifi or test, since transports are ORed; but there should only be wifi
1221         secondRequest.networkCapabilities.addTransportType(TRANSPORT_TEST);
1222 
1223         PendingIntent firstIntent = null;
1224         PendingIntent secondIntent = null;
1225         BroadcastReceiver receiver = null;
1226 
1227         // Avoid receiving broadcasts from other runs by appending a timestamp
1228         final String broadcastAction = NETWORK_CALLBACK_ACTION + System.currentTimeMillis();
1229         try {
1230             // Intent is mutable to receive EXTRA_NETWORK_REQUEST from ConnectivityService
1231             final String extraBoolKey = "extra_bool";
1232             firstIntent = PendingIntent.getBroadcast(mContext,
1233                     0 /* requestCode */,
1234                     new Intent(broadcastAction).putExtra(extraBoolKey, false)
1235                             .setPackage(mContext.getPackageName()),
1236                     PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
1237 
1238             if (useListen) {
1239                 mCm.registerNetworkCallback(firstRequest, firstIntent);
1240             } else {
1241                 mCm.requestNetwork(firstRequest, firstIntent);
1242             }
1243 
1244             // Second intent equals the first as per filterEquals (extras don't count), so first
1245             // intent will be updated with the new extras
1246             secondIntent = PendingIntent.getBroadcast(mContext,
1247                     0 /* requestCode */,
1248                     new Intent(broadcastAction).putExtra(extraBoolKey, true)
1249                             .setPackage(mContext.getPackageName()),
1250                     PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
1251 
1252             // Because secondIntent.intentFilterEquals the first, the request should be replaced
1253             if (useListen) {
1254                 mCm.registerNetworkCallback(secondRequest, secondIntent);
1255             } else {
1256                 mCm.requestNetwork(secondRequest, secondIntent);
1257             }
1258 
1259             final IntentFilter filter = new IntentFilter();
1260             filter.addAction(broadcastAction);
1261 
1262             final CompletableFuture<NetworkRequest> requestFuture = new CompletableFuture<>();
1263             final CompletableFuture<Network> networkFuture = new CompletableFuture<>();
1264             final AtomicInteger receivedCount = new AtomicInteger(0);
1265             receiver = new BroadcastReceiver() {
1266                 @Override
1267                 public void onReceive(Context context, Intent intent) {
1268                     final NetworkRequest request = intent.getParcelableExtra(EXTRA_NETWORK_REQUEST);
1269                     requestFuture.complete(request);
1270                     receivedCount.incrementAndGet();
1271                     networkFuture.complete(intent.getParcelableExtra(EXTRA_NETWORK));
1272                 }
1273             };
1274             final int flags = SdkLevel.isAtLeastT() ? RECEIVER_EXPORTED : 0;
1275             mContext.registerReceiver(receiver, filter, flags);
1276 
1277             final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
1278             try {
1279                 assertEquals(wifiNetwork, networkFuture.get(
1280                         NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
1281             } catch (TimeoutException e) {
1282                 throw new AssertionError("PendingIntent not received for " + secondRequest, e);
1283             }
1284             assertPendingIntentRequestMatches(
1285                     requestFuture.get(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS),
1286                     secondRequest, useListen);
1287 
1288             // Sleep for a small amount of time to try to check that only one callback is ever
1289             // received (so the first callback was really unregistered). This does not guarantee
1290             // that the test will fail if it runs very slowly, but it should at least be very
1291             // noticeably flaky.
1292             Thread.sleep(NO_CALLBACK_TIMEOUT_MS);
1293 
1294             // For R- frameworks, listens will receive duplicated callbacks. See b/189868426.
1295             if (isAtLeastS() || !useListen) {
1296                 assertEquals("PendingIntent should only be received once", 1, receivedCount.get());
1297             }
1298         } finally {
1299             if (firstIntent != null) mCm.unregisterNetworkCallback(firstIntent);
1300             if (secondIntent != null) mCm.unregisterNetworkCallback(secondIntent);
1301             if (receiver != null) mContext.unregisterReceiver(receiver);
1302             mCtsNetUtils.ensureWifiConnected();
1303         }
1304     }
1305 
assertPendingIntentRequestMatches(NetworkRequest broadcasted, NetworkRequest filed, boolean useListen)1306     private void assertPendingIntentRequestMatches(NetworkRequest broadcasted, NetworkRequest filed,
1307             boolean useListen) {
1308         assertArrayEquals(filed.networkCapabilities.getCapabilities(),
1309                 broadcasted.networkCapabilities.getCapabilities());
1310         // For R- frameworks, listens will receive duplicated callbacks. See b/189868426.
1311         if (!isAtLeastS() && useListen) return;
1312         assertArrayEquals(filed.networkCapabilities.getTransportTypes(),
1313                 broadcasted.networkCapabilities.getTransportTypes());
1314     }
1315 
1316     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1317     @Test
testRegisterNetworkRequest_identicalPendingIntents()1318     public void testRegisterNetworkRequest_identicalPendingIntents() throws Exception {
1319         runIdenticalPendingIntentsRequestTest(false /* useListen */);
1320     }
1321 
1322     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1323     @Test
testRegisterNetworkCallback_identicalPendingIntents()1324     public void testRegisterNetworkCallback_identicalPendingIntents() throws Exception {
1325         runIdenticalPendingIntentsRequestTest(true /* useListen */);
1326     }
1327 
1328     /**
1329      * Exercises the requestNetwork with NetworkCallback API. This checks to
1330      * see if we get a callback for an INTERNET request.
1331      */
1332     @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
1333     @Test
testRequestNetworkCallback()1334     public void testRequestNetworkCallback() {
1335         final TestableNetworkCallback callback = networkCallbackRule.requestNetwork(
1336                 new NetworkRequest.Builder().addCapability(
1337                                 NET_CAPABILITY_INTERNET)
1338                         .build());
1339 
1340         // Wait to get callback for availability of internet
1341         callback.eventuallyExpect(CallbackEntry.AVAILABLE).getNetwork();
1342     }
1343 
1344     /**
1345      * Exercises the requestNetwork with NetworkCallback API with timeout - expected to
1346      * fail. Use WIFI and switch Wi-Fi off.
1347      */
1348     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1349     @Test
testRequestNetworkCallback_onUnavailable()1350     public void testRequestNetworkCallback_onUnavailable() {
1351         boolean previousWifiEnabledState = false;
1352         if (mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
1353             previousWifiEnabledState = mWifiManager.isWifiEnabled();
1354             if (previousWifiEnabledState) {
1355                 mCtsNetUtils.ensureWifiDisconnected(null);
1356             }
1357         }
1358 
1359 
1360         final TestableNetworkCallback callback = networkCallbackRule.requestNetwork(
1361                 new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build(),
1362                 100 /* timeoutMs */);
1363         try {
1364             // Wait to get callback for unavailability of requested network
1365             callback.eventuallyExpect(CallbackEntry.UNAVAILABLE, 2_000 /* timeoutMs */);
1366         } finally {
1367             if (previousWifiEnabledState) {
1368                 mCtsNetUtils.connectToWifi();
1369             }
1370         }
1371     }
1372 
getFirstV4Address(Network network)1373     private InetAddress getFirstV4Address(Network network) {
1374         LinkProperties linkProperties = mCm.getLinkProperties(network);
1375         for (InetAddress address : linkProperties.getAddresses()) {
1376             if (address instanceof Inet4Address) {
1377                 return address;
1378             }
1379         }
1380         return null;
1381     }
1382 
1383     /**
1384      * Checks that enabling/disabling wifi causes CONNECTIVITY_ACTION broadcasts.
1385      */
1386     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1387     @Test
testToggleWifiConnectivityAction()1388     public void testToggleWifiConnectivityAction() throws Exception {
1389         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
1390 
1391         mCtsNetUtils.reconnectWifiAndWaitForConnectivityAction();
1392     }
1393 
1394     /** Verify restricted networks cannot be requested. */
1395     @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
1396     @Test
testRestrictedNetworks()1397     public void testRestrictedNetworks() {
1398         // Verify we can request unrestricted networks:
1399         NetworkRequest request = new NetworkRequest.Builder()
1400                 .addCapability(NET_CAPABILITY_INTERNET).build();
1401         NetworkCallback callback = new NetworkCallback();
1402         mCm.requestNetwork(request, callback);
1403         mCm.unregisterNetworkCallback(callback);
1404         // Verify we cannot request restricted networks:
1405         request = new NetworkRequest.Builder().addCapability(NET_CAPABILITY_IMS).build();
1406         callback = new NetworkCallback();
1407         try {
1408             mCm.requestNetwork(request, callback);
1409             fail("No exception thrown when restricted network requested.");
1410         } catch (SecurityException expected) {}
1411     }
1412 
1413     // Returns "true", "false" or "none"
getWifiMeteredStatus(String ssid)1414     private String getWifiMeteredStatus(String ssid) throws Exception {
1415         // Interestingly giving the SSID as an argument to list wifi-networks
1416         // only works iff the network in question has the "false" policy.
1417         // Also unfortunately runShellCommand does not pass the command to the interpreter
1418         // so it's not possible to | grep the ssid.
1419         final String command = "cmd netpolicy list wifi-networks";
1420         final String policyString = runShellCommand(mInstrumentation, command);
1421 
1422         final Matcher m = Pattern.compile("^" + ssid + ";(true|false|none)$",
1423                 Pattern.MULTILINE | Pattern.UNIX_LINES).matcher(policyString);
1424         if (!m.find()) {
1425             fail("Unexpected format from cmd netpolicy, policyString = " + policyString);
1426         }
1427         return m.group(1);
1428     }
1429 
1430     // metered should be "true", "false" or "none"
setWifiMeteredStatus(String ssid, String metered)1431     private void setWifiMeteredStatus(String ssid, String metered) throws Exception {
1432         final String setCommand = "cmd netpolicy set metered-network " + ssid + " " + metered;
1433         runShellCommand(mInstrumentation, setCommand);
1434         assertEquals(getWifiMeteredStatus(ssid), metered);
1435     }
1436 
unquoteSSID(String ssid)1437     private String unquoteSSID(String ssid) {
1438         // SSID is returned surrounded by quotes if it can be decoded as UTF-8.
1439         // Otherwise it's guaranteed not to start with a quote.
1440         if (ssid.charAt(0) == '"') {
1441             return ssid.substring(1, ssid.length() - 1);
1442         } else {
1443             return ssid;
1444         }
1445     }
1446 
waitForActiveNetworkMetered(final int targetTransportType, final boolean requestedMeteredness, final boolean waitForValidation, final boolean useSystemDefault)1447     private Network waitForActiveNetworkMetered(final int targetTransportType,
1448             final boolean requestedMeteredness, final boolean waitForValidation,
1449             final boolean useSystemDefault)
1450             throws Exception {
1451         final CompletableFuture<Network> networkFuture = new CompletableFuture<>();
1452 
1453         // Registering a callback here guarantees onCapabilitiesChanged is called immediately
1454         // with the current setting. Therefore, if the setting has already been changed,
1455         // this method will return right away, and if not, it'll wait for the setting to change.
1456         final TestableNetworkCallback networkCallback;
1457         if (useSystemDefault) {
1458             networkCallback = runWithShellPermissionIdentity(() -> {
1459                 if (isAtLeastS()) {
1460                     return networkCallbackRule.registerSystemDefaultNetworkCallback(
1461                             new Handler(Looper.getMainLooper()));
1462                 } else {
1463                     // registerSystemDefaultNetworkCallback is only supported on S+.
1464                     return networkCallbackRule.requestNetwork(
1465                             new NetworkRequest.Builder()
1466                                     .clearCapabilities()
1467                                     .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
1468                                     .addCapability(NET_CAPABILITY_TRUSTED)
1469                                     .addCapability(NET_CAPABILITY_NOT_VPN)
1470                                     .addCapability(NET_CAPABILITY_INTERNET)
1471                                     .build(),
1472                             new TestableNetworkCallback(),
1473                             new Handler(Looper.getMainLooper()));
1474                 }
1475             },
1476             NETWORK_SETTINGS);
1477         } else {
1478             networkCallback = networkCallbackRule.registerDefaultNetworkCallback();
1479         }
1480 
1481         return networkCallback.eventuallyExpect(
1482                 CallbackEntry.NETWORK_CAPS_UPDATED,
1483                 // Changing meteredness on wifi involves reconnecting, which can take several
1484                 // seconds (involves re-associating, DHCP...).
1485                 NETWORK_CALLBACK_TIMEOUT_MS,
1486                 cb -> {
1487                     final NetworkCapabilities nc = cb.getCaps();
1488                     if (!nc.hasTransport(targetTransportType)) return false;
1489 
1490                     final boolean metered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
1491                     final boolean validated = nc.hasCapability(NET_CAPABILITY_VALIDATED);
1492                     return metered == requestedMeteredness && (!waitForValidation || validated);
1493                 }).getNetwork();
1494     }
1495 
setWifiMeteredStatusAndWait(String ssid, boolean isMetered, boolean waitForValidation)1496     private Network setWifiMeteredStatusAndWait(String ssid, boolean isMetered,
1497             boolean waitForValidation) throws Exception {
1498         setWifiMeteredStatus(ssid, Boolean.toString(isMetered) /* metered */);
1499         mCtsNetUtils.ensureWifiConnected();
1500         return waitForActiveNetworkMetered(TRANSPORT_WIFI,
1501                 isMetered /* requestedMeteredness */,
1502                 waitForValidation,
1503                 true /* useSystemDefault */);
1504     }
1505 
assertMultipathPreferenceIsEventually(Network network, int oldValue, int expectedValue)1506     private void assertMultipathPreferenceIsEventually(Network network, int oldValue,
1507             int expectedValue) {
1508         // Quick check : if oldValue == expectedValue, there is no way to guarantee the test
1509         // is not flaky.
1510         assertNotSame(oldValue, expectedValue);
1511 
1512         for (int i = 0; i < NUM_TRIES_MULTIPATH_PREF_CHECK; ++i) {
1513             final int actualValue = mCm.getMultipathPreference(network);
1514             if (actualValue == expectedValue) {
1515                 return;
1516             }
1517             if (actualValue != oldValue) {
1518                 fail("Multipath preference is neither previous (" + oldValue
1519                         + ") nor expected (" + expectedValue + ")");
1520             }
1521             SystemClock.sleep(INTERVAL_MULTIPATH_PREF_CHECK_MS);
1522         }
1523         fail("Timed out waiting for multipath preference to change. expected = "
1524                 + expectedValue + " ; actual = " + mCm.getMultipathPreference(network));
1525     }
1526 
getCurrentMeteredMultipathPreference(ContentResolver resolver)1527     private int getCurrentMeteredMultipathPreference(ContentResolver resolver) {
1528         final String rawMeteredPref = Settings.Global.getString(resolver,
1529                 NETWORK_METERED_MULTIPATH_PREFERENCE);
1530         return TextUtils.isEmpty(rawMeteredPref)
1531             ? getIntResourceForName(NETWORK_METERED_MULTIPATH_PREFERENCE_RES_NAME)
1532             : Integer.parseInt(rawMeteredPref);
1533     }
1534 
findNextPrefValue(ContentResolver resolver)1535     private int findNextPrefValue(ContentResolver resolver) {
1536         // A bit of a nuclear hammer, but race conditions in CTS are bad. To be able to
1537         // detect a correct setting value without race conditions, the next pref must
1538         // be a valid value (range 0..3) that is different from the old setting of the
1539         // metered preference and from the unmetered preference.
1540         final int meteredPref = getCurrentMeteredMultipathPreference(resolver);
1541         final int unmeteredPref = ConnectivityManager.MULTIPATH_PREFERENCE_UNMETERED;
1542         if (0 != meteredPref && 0 != unmeteredPref) return 0;
1543         if (1 != meteredPref && 1 != unmeteredPref) return 1;
1544         return 2;
1545     }
1546 
1547     /**
1548      * Verify that getMultipathPreference does return appropriate values
1549      * for metered and unmetered networks.
1550      */
1551     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1552     @Test
testGetMultipathPreference()1553     public void testGetMultipathPreference() throws Exception {
1554         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
1555         final ContentResolver resolver = mContext.getContentResolver();
1556         mCtsNetUtils.ensureWifiConnected();
1557         final String ssid = unquoteSSID(mWifiManager.getConnectionInfo().getSSID());
1558         final String oldMeteredSetting = getWifiMeteredStatus(ssid);
1559         final String oldMeteredMultipathPreference = Settings.Global.getString(
1560                 resolver, NETWORK_METERED_MULTIPATH_PREFERENCE);
1561         try {
1562             final int initialMeteredPreference = getCurrentMeteredMultipathPreference(resolver);
1563             int newMeteredPreference = findNextPrefValue(resolver);
1564             Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
1565                     Integer.toString(newMeteredPreference));
1566             // Wifi meteredness changes from unmetered to metered will disconnect and reconnect
1567             // since R.
1568             final Network network = setWifiMeteredStatusAndWait(ssid, true /* isMetered */,
1569                     false /* waitForValidation */);
1570             assertEquals(ssid, unquoteSSID(mWifiManager.getConnectionInfo().getSSID()));
1571             assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
1572                     NET_CAPABILITY_NOT_METERED), false);
1573             assertMultipathPreferenceIsEventually(network, initialMeteredPreference,
1574                     newMeteredPreference);
1575 
1576             final int oldMeteredPreference = newMeteredPreference;
1577             newMeteredPreference = findNextPrefValue(resolver);
1578             Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
1579                     Integer.toString(newMeteredPreference));
1580             assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
1581                     NET_CAPABILITY_NOT_METERED), false);
1582             assertMultipathPreferenceIsEventually(network,
1583                     oldMeteredPreference, newMeteredPreference);
1584 
1585             // No disconnect from unmetered to metered.
1586             setWifiMeteredStatusAndWait(ssid, false /* isMetered */, false /* waitForValidation */);
1587             assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
1588                     NET_CAPABILITY_NOT_METERED), true);
1589             assertMultipathPreferenceIsEventually(network, newMeteredPreference,
1590                     ConnectivityManager.MULTIPATH_PREFERENCE_UNMETERED);
1591         } finally {
1592             Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
1593                     oldMeteredMultipathPreference);
1594             setWifiMeteredStatus(ssid, oldMeteredSetting);
1595         }
1596     }
1597 
1598     @Test @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @ConnectivityModuleTest
testSetBackgroundNetworkingShellCommand()1599     public void testSetBackgroundNetworkingShellCommand() {
1600         final int testUid = 54352;
1601         runShellCommand("cmd connectivity set-background-networking-enabled-for-uid " + testUid
1602                 + " true");
1603         int rule = runAsShell(NETWORK_SETTINGS,
1604                 () -> mCm.getUidFirewallRule(FIREWALL_CHAIN_BACKGROUND, testUid));
1605         assertEquals(rule, FIREWALL_RULE_ALLOW);
1606 
1607         runShellCommand("cmd connectivity set-background-networking-enabled-for-uid " + testUid
1608                 + " false");
1609         rule = runAsShell(NETWORK_SETTINGS,
1610                 () -> mCm.getUidFirewallRule(FIREWALL_CHAIN_BACKGROUND, testUid));
1611         assertEquals(rule, FIREWALL_RULE_DENY);
1612     }
1613 
1614     @Test @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @ConnectivityModuleTest
testGetBackgroundNetworkingShellCommand()1615     public void testGetBackgroundNetworkingShellCommand() {
1616         final int testUid = 54312;
1617         runAsShell(NETWORK_SETTINGS,
1618                 () -> mCm.setUidFirewallRule(FIREWALL_CHAIN_BACKGROUND, testUid,
1619                         FIREWALL_RULE_ALLOW));
1620         String output = runShellCommand(
1621                 "cmd connectivity get-background-networking-enabled-for-uid " + testUid);
1622         assertTrue(output.contains("allow"));
1623 
1624         runAsShell(NETWORK_SETTINGS,
1625                 () -> mCm.setUidFirewallRule(FIREWALL_CHAIN_BACKGROUND, testUid,
1626                         FIREWALL_RULE_DEFAULT));
1627         output = runShellCommand(
1628                 "cmd connectivity get-background-networking-enabled-for-uid " + testUid);
1629         assertTrue(output.contains("deny"));
1630     }
1631 
1632     // TODO: move the following socket keep alive test to dedicated test class.
1633     /**
1634      * Callback used in tcp keepalive offload that allows caller to wait callback fires.
1635      */
1636     private static class TestSocketKeepaliveCallback extends SocketKeepalive.Callback {
1637         public enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
1638 
1639         public static class CallbackValue {
1640             public final CallbackType callbackType;
1641             public final int error;
1642 
CallbackValue(final CallbackType type, final int error)1643             private CallbackValue(final CallbackType type, final int error) {
1644                 this.callbackType = type;
1645                 this.error = error;
1646             }
1647 
1648             public static class OnStartedCallback extends CallbackValue {
OnStartedCallback()1649                 OnStartedCallback() { super(CallbackType.ON_STARTED, 0); }
1650             }
1651 
1652             public static class OnStoppedCallback extends CallbackValue {
OnStoppedCallback()1653                 OnStoppedCallback() { super(CallbackType.ON_STOPPED, 0); }
1654             }
1655 
1656             public static class OnErrorCallback extends CallbackValue {
OnErrorCallback(final int error)1657                 OnErrorCallback(final int error) { super(CallbackType.ON_ERROR, error); }
1658             }
1659 
1660             @Override
equals(Object o)1661             public boolean equals(Object o) {
1662                 return o.getClass() == this.getClass()
1663                         && this.callbackType == ((CallbackValue) o).callbackType
1664                         && this.error == ((CallbackValue) o).error;
1665             }
1666 
1667             @Override
toString()1668             public String toString() {
1669                 return String.format("%s(%s, %d)", getClass().getSimpleName(), callbackType, error);
1670             }
1671         }
1672 
1673         private final LinkedBlockingQueue<CallbackValue> mCallbacks = new LinkedBlockingQueue<>();
1674 
1675         @Override
onStarted()1676         public void onStarted() {
1677             mCallbacks.add(new CallbackValue.OnStartedCallback());
1678         }
1679 
1680         @Override
onStopped()1681         public void onStopped() {
1682             mCallbacks.add(new CallbackValue.OnStoppedCallback());
1683         }
1684 
1685         @Override
onError(final int error)1686         public void onError(final int error) {
1687             mCallbacks.add(new CallbackValue.OnErrorCallback(error));
1688         }
1689 
pollCallback()1690         public CallbackValue pollCallback() {
1691             try {
1692                 return mCallbacks.poll(KEEPALIVE_CALLBACK_TIMEOUT_MS,
1693                         TimeUnit.MILLISECONDS);
1694             } catch (InterruptedException e) {
1695                 fail("Callback not seen after " + KEEPALIVE_CALLBACK_TIMEOUT_MS + " ms");
1696             }
1697             return null;
1698         }
expectCallback(CallbackValue expectedCallback)1699         private void expectCallback(CallbackValue expectedCallback) {
1700             final CallbackValue actualCallback = pollCallback();
1701             assertEquals(expectedCallback, actualCallback);
1702         }
1703 
expectStarted()1704         public void expectStarted() {
1705             expectCallback(new CallbackValue.OnStartedCallback());
1706         }
1707 
expectStopped()1708         public void expectStopped() {
1709             expectCallback(new CallbackValue.OnStoppedCallback());
1710         }
1711 
expectError(int error)1712         public void expectError(int error) {
1713             expectCallback(new CallbackValue.OnErrorCallback(error));
1714         }
1715     }
1716 
getAddrByName(final String hostname, final int family)1717     private InetAddress getAddrByName(final String hostname, final int family) throws Exception {
1718         final InetAddress[] allAddrs = InetAddress.getAllByName(hostname);
1719         for (InetAddress addr : allAddrs) {
1720             if (family == AF_INET && addr instanceof Inet4Address) return addr;
1721 
1722             if (family == AF_INET6 && addr instanceof Inet6Address) return addr;
1723 
1724             if (family == AF_UNSPEC) return addr;
1725         }
1726         return null;
1727     }
1728 
getConnectedSocket(final Network network, final String host, final int port, final int family)1729     private Socket getConnectedSocket(final Network network, final String host, final int port,
1730             final int family) throws Exception {
1731         final Socket s = network.getSocketFactory().createSocket();
1732         try {
1733             final InetAddress addr = getAddrByName(host, family);
1734             if (addr == null) fail("Fail to get destination address for " + family);
1735 
1736             final InetSocketAddress sockAddr = new InetSocketAddress(addr, port);
1737             s.connect(sockAddr);
1738         } catch (Exception e) {
1739             s.close();
1740             throw e;
1741         }
1742         return s;
1743     }
1744 
getSupportedKeepalivesForNet(@onNull Network network)1745     private int getSupportedKeepalivesForNet(@NonNull Network network) throws Exception {
1746         final NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
1747 
1748         // Get number of supported concurrent keepalives for testing network.
1749         final int[] keepalivesPerTransport = runAsShell(NETWORK_SETTINGS,
1750                 () -> mCm.getSupportedKeepalives());
1751         return KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(
1752                 keepalivesPerTransport, nc);
1753     }
1754 
isTcpKeepaliveSupportedByKernel()1755     private static boolean isTcpKeepaliveSupportedByKernel() {
1756         final String kVersionString = VintfRuntimeInfo.getKernelVersion();
1757         return DeviceInfoUtils.compareMajorMinorVersion(kVersionString, "4.8") >= 0;
1758     }
1759 
1760     /**
1761      * Verifies that the keepalive API cannot create any keepalive when the maximum number of
1762      * keepalives is set to 0.
1763      */
1764     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1765     // getSupportedKeepalives is available in updatable ConnectivityManager (S+)
1766     // Also, this feature is not mainlined before S, and it's fine to skip on R- devices.
1767     @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) @ConnectivityModuleTest
1768     @Test
testKeepaliveWifiUnsupported()1769     public void testKeepaliveWifiUnsupported() throws Exception {
1770         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
1771 
1772         final Network network = mCtsNetUtils.ensureWifiConnected();
1773         if (getSupportedKeepalivesForNet(network) != 0) return;
1774         final InetAddress srcAddr = getFirstV4Address(network);
1775         assumeTrue("This test requires native IPv4", srcAddr != null);
1776 
1777         runWithShellPermissionIdentity(() -> {
1778             assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 1, 0));
1779             assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 0, 1));
1780         });
1781     }
1782 
1783     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1784     // getSupportedKeepalives is available in updatable ConnectivityManager (S+)
1785     // Also, this feature is not mainlined before S, and it's fine to skip on R- devices.
1786     @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) @ConnectivityModuleTest
1787     @Test
1788     @RequiresDevice // Keepalive is not supported on virtual hardware
testCreateTcpKeepalive()1789     public void testCreateTcpKeepalive() throws Exception {
1790         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
1791 
1792         final Network network = mCtsNetUtils.ensureWifiConnected();
1793         if (getSupportedKeepalivesForNet(network) == 0) return;
1794         final InetAddress srcAddr = getFirstV4Address(network);
1795         assumeTrue("This test requires native IPv4", srcAddr != null);
1796 
1797         // If kernel < 4.8 then it doesn't support TCP keepalive, but it might still support
1798         // NAT-T keepalive. If keepalive limits from resource overlay is not zero, TCP keepalive
1799         // needs to be supported except if the kernel doesn't support it.
1800         if (!isTcpKeepaliveSupportedByKernel()) {
1801             // Verify that the callback result is expected.
1802             runWithShellPermissionIdentity(() -> {
1803                 assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 0, 1));
1804             });
1805             Log.i(TAG, "testCreateTcpKeepalive is skipped for kernel "
1806                     + VintfRuntimeInfo.getKernelVersion());
1807             return;
1808         }
1809 
1810         final byte[] requestBytes = CtsNetUtils.HTTP_REQUEST.getBytes("UTF-8");
1811         // So far only ipv4 tcp keepalive offload is supported.
1812         // TODO: add test case for ipv6 tcp keepalive offload when it is supported.
1813         try (Socket s = getConnectedSocket(network, TEST_HOST, HTTP_PORT, AF_INET)) {
1814 
1815             // Should able to start keep alive offload when socket is idle.
1816             final Executor executor = mContext.getMainExecutor();
1817             final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
1818 
1819             mUiAutomation.adoptShellPermissionIdentity();
1820             try (SocketKeepalive sk = mCm.createSocketKeepalive(network, s, executor, callback)) {
1821                 sk.start(MIN_KEEPALIVE_INTERVAL);
1822                 callback.expectStarted();
1823 
1824                 // App should not able to write during keepalive offload.
1825                 final OutputStream out = s.getOutputStream();
1826                 try {
1827                     out.write(requestBytes);
1828                     fail("Should not able to write");
1829                 } catch (IOException e) { }
1830                 // App should not able to read during keepalive offload.
1831                 final InputStream in = s.getInputStream();
1832                 byte[] responseBytes = new byte[4096];
1833                 try {
1834                     in.read(responseBytes);
1835                     fail("Should not able to read");
1836                 } catch (IOException e) { }
1837 
1838                 // Stop.
1839                 sk.stop();
1840                 callback.expectStopped();
1841             } finally {
1842                 mUiAutomation.dropShellPermissionIdentity();
1843             }
1844 
1845             // Ensure socket is still connected.
1846             assertTrue(s.isConnected());
1847             assertFalse(s.isClosed());
1848 
1849             // Let socket be not idle.
1850             try {
1851                 final OutputStream out = s.getOutputStream();
1852                 out.write(requestBytes);
1853             } catch (IOException e) {
1854                 fail("Failed to write data " + e);
1855             }
1856             // Make sure response data arrives.
1857             final MessageQueue fdHandlerQueue = Looper.getMainLooper().getQueue();
1858             final FileDescriptor fd = s.getFileDescriptor$();
1859             final CountDownLatch mOnReceiveLatch = new CountDownLatch(1);
1860             fdHandlerQueue.addOnFileDescriptorEventListener(fd, EVENT_INPUT, (readyFd, events) -> {
1861                 mOnReceiveLatch.countDown();
1862                 return 0; // Unregister listener.
1863             });
1864             if (!mOnReceiveLatch.await(2, TimeUnit.SECONDS)) {
1865                 fdHandlerQueue.removeOnFileDescriptorEventListener(fd);
1866                 fail("Timeout: no response data");
1867             }
1868 
1869             // Should get ERROR_SOCKET_NOT_IDLE because there is still data in the receive queue
1870             // that has not been read.
1871             mUiAutomation.adoptShellPermissionIdentity();
1872             try (SocketKeepalive sk = mCm.createSocketKeepalive(network, s, executor, callback)) {
1873                 sk.start(MIN_KEEPALIVE_INTERVAL);
1874                 callback.expectError(SocketKeepalive.ERROR_SOCKET_NOT_IDLE);
1875             } finally {
1876                 mUiAutomation.dropShellPermissionIdentity();
1877             }
1878         }
1879     }
1880 
createConcurrentKeepalivesOfType( int requestCount, @NonNull TestSocketKeepaliveCallback callback, Supplier<SocketKeepalive> kaFactory)1881     private ArrayList<SocketKeepalive> createConcurrentKeepalivesOfType(
1882             int requestCount, @NonNull TestSocketKeepaliveCallback callback,
1883             Supplier<SocketKeepalive> kaFactory) {
1884         final ArrayList<SocketKeepalive> kalist = new ArrayList<>();
1885 
1886         int remainingRetries = MAX_KEEPALIVE_RETRY_COUNT;
1887 
1888         // Test concurrent keepalives with the given supplier.
1889         while (kalist.size() < requestCount) {
1890             final SocketKeepalive ka = kaFactory.get();
1891             ka.start(MIN_KEEPALIVE_INTERVAL);
1892             TestSocketKeepaliveCallback.CallbackValue cv = callback.pollCallback();
1893             assertNotNull(cv);
1894             if (cv.callbackType == TestSocketKeepaliveCallback.CallbackType.ON_ERROR) {
1895                 if (kalist.size() == 0 && cv.error == SocketKeepalive.ERROR_UNSUPPORTED) {
1896                     // Unsupported.
1897                     break;
1898                 } else if (cv.error == SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES) {
1899                     // Limit reached or temporary unavailable due to stopped slot is not yet
1900                     // released.
1901                     if (remainingRetries > 0) {
1902                         SystemClock.sleep(INTERVAL_KEEPALIVE_RETRY_MS);
1903                         remainingRetries--;
1904                         continue;
1905                     }
1906                     break;
1907                 }
1908             }
1909             if (cv.callbackType == TestSocketKeepaliveCallback.CallbackType.ON_STARTED) {
1910                 kalist.add(ka);
1911             } else {
1912                 fail("Unexpected error when creating " + (kalist.size() + 1) + " "
1913                         + ka.getClass().getSimpleName() + ": " + cv);
1914             }
1915         }
1916 
1917         return kalist;
1918     }
1919 
createConcurrentNattSocketKeepalives( @onNull Network network, @NonNull InetAddress srcAddr, int requestCount, @NonNull TestSocketKeepaliveCallback callback)1920     private @NonNull ArrayList<SocketKeepalive> createConcurrentNattSocketKeepalives(
1921             @NonNull Network network, @NonNull InetAddress srcAddr, int requestCount,
1922             @NonNull TestSocketKeepaliveCallback callback)  throws Exception {
1923 
1924         final Executor executor = mContext.getMainExecutor();
1925 
1926         // Initialize a real NaT-T socket.
1927         final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
1928         final UdpEncapsulationSocket nattSocket = mIpSec.openUdpEncapsulationSocket();
1929         final InetAddress dstAddr = getAddrByName(TEST_HOST, AF_INET);
1930         assertNotNull(srcAddr);
1931         assertNotNull(dstAddr);
1932 
1933         // Test concurrent Nat-T keepalives.
1934         final ArrayList<SocketKeepalive> result = createConcurrentKeepalivesOfType(requestCount,
1935                 callback, () -> mCm.createSocketKeepalive(network, nattSocket,
1936                         srcAddr, dstAddr, executor, callback));
1937 
1938         nattSocket.close();
1939         return result;
1940     }
1941 
createConcurrentTcpSocketKeepalives( @onNull Network network, int requestCount, @NonNull TestSocketKeepaliveCallback callback)1942     private @NonNull ArrayList<SocketKeepalive> createConcurrentTcpSocketKeepalives(
1943             @NonNull Network network, int requestCount,
1944             @NonNull TestSocketKeepaliveCallback callback) {
1945         final Executor executor = mContext.getMainExecutor();
1946 
1947         // Create concurrent TCP keepalives.
1948         return createConcurrentKeepalivesOfType(requestCount, callback, () -> {
1949             // Assert that TCP connections can be established. The file descriptor of tcp
1950             // sockets will be duplicated and kept valid in service side if the keepalives are
1951             // successfully started.
1952             try (Socket tcpSocket = getConnectedSocket(network, TEST_HOST, HTTP_PORT,
1953                     AF_INET)) {
1954                 return mCm.createSocketKeepalive(network, tcpSocket, executor, callback);
1955             } catch (Exception e) {
1956                 fail("Unexpected error when creating TCP socket: " + e);
1957             }
1958             return null;
1959         });
1960     }
1961 
1962     /**
1963      * Creates concurrent keepalives until the specified counts of each type of keepalives are
1964      * reached or the expected error callbacks are received for each type of keepalives.
1965      *
1966      * @return the total number of keepalives created.
1967      */
1968     private int createConcurrentSocketKeepalives(
1969             @NonNull Network network, @NonNull InetAddress srcAddr, int nattCount, int tcpCount)
1970             throws Exception {
1971         final ArrayList<SocketKeepalive> kalist = new ArrayList<>();
1972         final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
1973 
1974         kalist.addAll(createConcurrentNattSocketKeepalives(network, srcAddr, nattCount, callback));
1975         kalist.addAll(createConcurrentTcpSocketKeepalives(network, tcpCount, callback));
1976 
1977         final int ret = kalist.size();
1978 
1979         // Clean up.
1980         for (final SocketKeepalive ka : kalist) {
1981             ka.stop();
1982             callback.expectStopped();
1983         }
1984         kalist.clear();
1985 
1986         return ret;
1987     }
1988 
1989     /**
1990      * Verifies that the concurrent keepalive slots meet the minimum requirement, and don't
1991      * get leaked after iterations.
1992      */
1993     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1994     @Test
1995     // getSupportedKeepalives is available in updatable ConnectivityManager (S+)
1996     // Also, this feature is not mainlined before S, and it's fine to skip on R- devices.
1997     @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) @ConnectivityModuleTest
1998     @RequiresDevice // Keepalive is not supported on virtual hardware
1999     public void testSocketKeepaliveLimitWifi() throws Exception {
2000         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
2001 
2002         final Network network = mCtsNetUtils.ensureWifiConnected();
2003         final int supported = getSupportedKeepalivesForNet(network);
2004         if (supported == 0) {
2005             return;
2006         }
2007         final InetAddress srcAddr = getFirstV4Address(network);
2008         assumeTrue("This test requires native IPv4", srcAddr != null);
2009 
2010         runWithShellPermissionIdentity(() -> {
2011             // Verifies that the supported keepalive slots meet MIN_SUPPORTED_KEEPALIVE_COUNT.
2012             assertGreaterOrEqual(supported, MIN_SUPPORTED_WIFI_KEEPALIVE_COUNT);
2013 
2014             // Verifies that Nat-T keepalives can be established.
2015             assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
2016                     supported + 1, 0));
2017             // Verifies that keepalives don't get leaked in second round.
2018             assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, supported,
2019                     0));
2020         });
2021 
2022         // If kernel < 4.8 then it doesn't support TCP keepalive, but it might still support
2023         // NAT-T keepalive. Test below cases only if TCP keepalive is supported by kernel.
2024         if (!isTcpKeepaliveSupportedByKernel()) return;
2025 
2026         runWithShellPermissionIdentity(() -> {
2027             assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, 0,
2028                     supported + 1));
2029 
2030             // Verifies that different types can be established at the same time.
2031             assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
2032                     supported / 2, supported - supported / 2));
2033 
2034             // Verifies that keepalives don't get leaked in second round.
2035             assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, 0,
2036                     supported));
2037             assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
2038                     supported / 2, supported - supported / 2));
2039         });
2040     }
2041 
2042     /**
2043      * Verifies that the concurrent keepalive slots meet the minimum telephony requirement, and
2044      * don't get leaked after iterations.
2045      */
2046     @AppModeFull(reason = "Cannot request network in instant app mode")
2047     @Test
2048     // getSupportedKeepalives is available in updatable ConnectivityManager (S+)
2049     // Also, this feature is not mainlined before S, and it's fine to skip on R- devices.
2050     @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) @ConnectivityModuleTest
2051     @RequiresDevice // Keepalive is not supported on virtual hardware
2052     public void testSocketKeepaliveLimitTelephony() throws Exception {
2053         if (!mPackageManager.hasSystemFeature(FEATURE_TELEPHONY)) {
2054             Log.i(TAG, "testSocketKeepaliveLimitTelephony cannot execute unless device"
2055                     + " supports telephony");
2056             return;
2057         }
2058 
2059         final int firstSdk = SdkLevel.isAtLeastS()
2060                 ? Build.VERSION.DEVICE_INITIAL_SDK_INT
2061                 // FIRST_SDK_INT was a @TestApi field renamed to DEVICE_INITIAL_SDK_INT in S
2062                 : Build.VERSION.class.getField("FIRST_SDK_INT").getInt(null);
2063         if (firstSdk < Build.VERSION_CODES.Q) {
2064             Log.i(TAG, "testSocketKeepaliveLimitTelephony: skip test for devices launching"
2065                     + " before Q: " + firstSdk);
2066             return;
2067         }
2068 
2069         final Network network = networkCallbackRule.requestCell();
2070         final int supported = getSupportedKeepalivesForNet(network);
2071         final InetAddress srcAddr = getFirstV4Address(network);
2072         assumeTrue("This test requires native IPv4", srcAddr != null);
2073 
2074         runWithShellPermissionIdentity(() -> {
2075             // Verifies that the supported keepalive slots meet minimum requirement.
2076             assertGreaterOrEqual(supported, MIN_SUPPORTED_CELLULAR_KEEPALIVE_COUNT);
2077             // Verifies that Nat-T keepalives can be established.
2078             assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
2079                     supported + 1, 0));
2080             // Verifies that keepalives don't get leaked in second round.
2081             assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, supported,
2082                     0));
2083         });
2084     }
2085 
2086     private int getIntResourceForName(@NonNull String resName) {
2087         final Resources r = mContext.getResources();
2088         final int resId = r.getIdentifier(resName, "integer", "android");
2089         return r.getInteger(resId);
2090     }
2091 
2092     /**
2093      * Verifies that the keepalive slots are limited as customized for unprivileged requests.
2094      */
2095     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
2096     @Test
2097     // getSupportedKeepalives is available in updatable ConnectivityManager (S+)
2098     // Also, this feature is not mainlined before S, and it's fine to skip on R- devices.
2099     @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) @ConnectivityModuleTest
2100     @RequiresDevice // Keepalive is not supported on virtual hardware
2101     public void testSocketKeepaliveUnprivileged() throws Exception {
2102         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
2103 
2104         final Network network = mCtsNetUtils.ensureWifiConnected();
2105         final int supported = getSupportedKeepalivesForNet(network);
2106         if (supported == 0) {
2107             return;
2108         }
2109         final InetAddress srcAddr = getFirstV4Address(network);
2110         assumeTrue("This test requires native IPv4", srcAddr != null);
2111 
2112         // Resource ID might be shifted on devices that compiled with different symbols.
2113         // Thus, resolve ID at runtime is needed.
2114         final int allowedUnprivilegedPerUid =
2115                 getIntResourceForName(KEEPALIVE_ALLOWED_UNPRIVILEGED_RES_NAME);
2116         final int reservedPrivilegedSlots =
2117                 getIntResourceForName(KEEPALIVE_RESERVED_PER_SLOT_RES_NAME);
2118         // Verifies that unprivileged request per uid cannot exceed the limit customized in the
2119         // resource. Currently, unprivileged keepalive slots are limited to Nat-T only, this test
2120         // does not apply to TCP.
2121         assertGreaterOrEqual(supported, reservedPrivilegedSlots);
2122         assertGreaterOrEqual(supported, allowedUnprivilegedPerUid);
2123         final int expectedUnprivileged =
2124                 Math.min(allowedUnprivilegedPerUid, supported - reservedPrivilegedSlots);
2125         assertEquals(expectedUnprivileged,
2126                 createConcurrentSocketKeepalives(network, srcAddr, supported + 1, 0));
2127     }
2128 
2129     private static void assertGreaterOrEqual(long greater, long lesser) {
2130         assertTrue("" + greater + " expected to be greater than or equal to " + lesser,
2131                 greater >= lesser);
2132     }
2133 
2134     private void verifyBindSocketToRestrictedNetworkDisallowed() throws Exception {
2135         final NetworkRequest testRequest = new NetworkRequest.Builder()
2136                 .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
2137                 .removeCapability(NET_CAPABILITY_TRUSTED)
2138                 .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
2139                 .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
2140                         TEST_RESTRICTED_NW_IFACE_NAME))
2141                 .build();
2142         final TestableNetworkCallback testNetworkCb = runWithShellPermissionIdentity(
2143                 () -> networkCallbackRule.requestNetwork(testRequest),
2144                 CONNECTIVITY_USE_RESTRICTED_NETWORKS,
2145                 // CONNECTIVITY_INTERNAL is for requesting restricted network because shell does not
2146                 // have CONNECTIVITY_USE_RESTRICTED_NETWORKS on R.
2147                 CONNECTIVITY_INTERNAL);
2148 
2149         // Create a restricted network and ensure this package cannot bind to that network either.
2150         final NetworkAgent agent = createRestrictedNetworkAgent(mContext);
2151         final Network network = agent.getNetwork();
2152 
2153         try (Socket socket = new Socket()) {
2154             // Verify that the network is restricted.
2155             testNetworkCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
2156                     NETWORK_CALLBACK_TIMEOUT_MS,
2157                     entry -> network.equals(entry.getNetwork())
2158                             && (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
2159                             .hasCapability(NET_CAPABILITY_NOT_RESTRICTED)));
2160             // CtsNetTestCases package doesn't hold CONNECTIVITY_USE_RESTRICTED_NETWORKS, so it
2161             // does not allow to bind socket to restricted network.
2162             assertThrows(IOException.class, () -> network.bindSocket(socket));
2163         } finally {
2164             agent.unregister();
2165         }
2166     }
2167 
2168     /**
2169      * Verifies that apps are not allowed to access restricted networks even if they declare the
2170      * CONNECTIVITY_USE_RESTRICTED_NETWORKS permission in their manifests.
2171      * See. b/144679405.
2172      */
2173     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
2174     @Test
2175     @IgnoreUpTo(Build.VERSION_CODES.Q)
2176     public void testRestrictedNetworkPermission() throws Exception {
2177         // Ensure that CONNECTIVITY_USE_RESTRICTED_NETWORKS isn't granted to this package.
2178         final PackageInfo app = mPackageManager.getPackageInfo(mContext.getPackageName(),
2179                 GET_PERMISSIONS);
2180         final int index = ArrayUtils.indexOf(
2181                 app.requestedPermissions, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
2182         assertTrue(index >= 0);
2183         assertTrue(app.requestedPermissionsFlags[index] != PERMISSION_GRANTED);
2184 
2185         if (mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
2186             // Expect binding to the wifi network to succeed.
2187             final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
2188             try (Socket socket = new Socket()) {
2189                 wifiNetwork.bindSocket(socket);
2190             }
2191         }
2192 
2193         // Ensure that this package cannot bind to any restricted network that's currently
2194         // connected.
2195         Network[] networks = mCm.getAllNetworks();
2196         for (Network network : networks) {
2197             final NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
2198             if (nc == null) {
2199                 continue;
2200             }
2201 
2202             try (Socket socket = new Socket()) {
2203                 if (nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
2204                     network.bindSocket(socket);  // binding should succeed
2205                 } else {
2206                     assertThrows(IOException.class, () -> network.bindSocket(socket));
2207                 }
2208             }
2209         }
2210 
2211         verifyBindSocketToRestrictedNetworkDisallowed();
2212     }
2213 
2214     /**
2215      * Verifies that apps are allowed to call setAirplaneMode if they declare
2216      * NETWORK_AIRPLANE_MODE permission in their manifests.
2217      * See b/145164696.
2218      */
2219     @AppModeFull(reason = "NETWORK_AIRPLANE_MODE permission can't be granted to instant apps")
2220     @Test
2221     @SkipPresubmit(reason = "Out of SLO flakiness")
2222     public void testSetAirplaneMode() throws Exception{
2223         // Starting from T, wifi supports airplane mode enhancement which may not disconnect wifi
2224         // when airplane mode is on. The actual behavior that the device will have could only be
2225         // checked with hidden wifi APIs(see Settings.Secure.WIFI_APM_STATE). Thus, stop verifying
2226         // wifi on T+ device.
2227         final boolean verifyWifi = mPackageManager.hasSystemFeature(FEATURE_WIFI)
2228                 && !SdkLevel.isAtLeastT();
2229         final boolean supportTelephony = mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
2230         // store the current state of airplane mode
2231         final boolean isAirplaneModeEnabled = isAirplaneModeEnabled();
2232         final TestableNetworkCallback wifiCb = new TestableNetworkCallback();
2233         final TestableNetworkCallback telephonyCb = new TestableNetworkCallback();
2234         // disable airplane mode to reach a known state
2235         runShellCommand("cmd connectivity airplane-mode disable");
2236         // Verify that networks are available as expected if wifi or cell is supported. Continue the
2237         // test if none of them are supported since test should still able to verify the permission
2238         // mechanism.
2239         if (verifyWifi) {
2240             mCtsNetUtils.ensureWifiConnected();
2241             registerCallbackAndWaitForAvailable(makeWifiNetworkRequest(), wifiCb);
2242         }
2243         if (supportTelephony) {
2244             networkCallbackRule.requestCell();
2245             registerCallbackAndWaitForAvailable(makeCellNetworkRequest(), telephonyCb);
2246         }
2247 
2248         try {
2249             // Verify we cannot set Airplane Mode without correct permission:
2250             assertThrows(SecurityException.class, () -> setAndVerifyAirplaneMode(true));
2251 
2252             // disable airplane mode again to reach a known state
2253             runShellCommand("cmd connectivity airplane-mode disable");
2254 
2255             // Verify we can enable Airplane Mode with correct permission.
2256             // TODO: test that NETWORK_AIRPLANE_MODE works as well, once the shell has it.
2257             runAsShell(NETWORK_SETTINGS, () -> setAndVerifyAirplaneMode(true));
2258 
2259             // Verify that the enabling airplane mode takes effect as expected to prevent flakiness
2260             // caused by fast airplane mode switches. Ensure network lost before turning off
2261             // airplane mode.
2262             if (verifyWifi) waitForLost(wifiCb);
2263             if (supportTelephony) waitForLost(telephonyCb);
2264 
2265             // Verify we can disable Airplane Mode with correct permission:
2266             runAsShell(NETWORK_SETTINGS, () -> setAndVerifyAirplaneMode(false));
2267 
2268             // Verify that turning airplane mode off takes effect as expected.
2269             // connectToCell only registers a request, it cannot / does not need to be called twice
2270             mCtsNetUtils.ensureWifiConnected();
2271             if (verifyWifi) waitForAvailable(wifiCb);
2272             if (supportTelephony) {
2273                 telephonyCb.eventuallyExpect(
2274                         CallbackEntry.AVAILABLE, CELL_DATA_AVAILABLE_TIMEOUT_MS);
2275             }
2276         } finally {
2277             // Restore the previous state of airplane mode and permissions:
2278             runShellCommand("cmd connectivity airplane-mode "
2279                     + (isAirplaneModeEnabled ? "enable" : "disable"));
2280         }
2281     }
2282 
2283     private void registerCallbackAndWaitForAvailable(@NonNull final NetworkRequest request,
2284             @NonNull final TestableNetworkCallback cb) {
2285         networkCallbackRule.registerNetworkCallback(request, cb);
2286         waitForAvailable(cb);
2287     }
2288 
2289     private void waitForAvailable(@NonNull final TestableNetworkCallback cb) {
2290         cb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
2291                 c -> c instanceof CallbackEntry.Available);
2292     }
2293 
2294     private void waitForTransport(
2295             @NonNull final TestableNetworkCallback cb, final int expectedTransport) {
2296         cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
2297                 NETWORK_CALLBACK_TIMEOUT_MS,
2298                 entry -> ((CallbackEntry.CapabilitiesChanged) entry).getCaps()
2299                         .hasTransport(expectedTransport));
2300     }
2301 
2302     private void waitForAvailable(
2303             @NonNull final TestableNetworkCallback cb, @NonNull final Network expectedNetwork) {
2304         cb.expectAvailableCallbacks(expectedNetwork, false /* suspended */,
2305                 null /* validated */,
2306                 false /* blocked */, NETWORK_CALLBACK_TIMEOUT_MS);
2307     }
2308 
2309     private void waitForLost(@NonNull final TestableNetworkCallback cb) {
2310         cb.eventuallyExpect(CallbackEntry.LOST, NETWORK_CALLBACK_TIMEOUT_MS,
2311                 c -> c instanceof CallbackEntry.Lost);
2312     }
2313 
2314     private void setAndVerifyAirplaneMode(Boolean expectedResult)
2315             throws Exception {
2316         final CompletableFuture<Boolean> actualResult = new CompletableFuture();
2317         BroadcastReceiver receiver = new BroadcastReceiver() {
2318             @Override
2319             public void onReceive(Context context, Intent intent) {
2320                 // The defaultValue of getExtraBoolean should be the opposite of what is
2321                 // expected, thus ensuring a test failure if the extra is absent.
2322                 actualResult.complete(intent.getBooleanExtra("state", !expectedResult));
2323             }
2324         };
2325         try {
2326             mContext.registerReceiver(receiver,
2327                     new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
2328             mCm.setAirplaneMode(expectedResult);
2329             final String msg = "Setting Airplane Mode failed,";
2330             assertEquals(msg, expectedResult, actualResult.get(AIRPLANE_MODE_CHANGE_TIMEOUT_MS,
2331                     TimeUnit.MILLISECONDS));
2332         } finally {
2333             mContext.unregisterReceiver(receiver);
2334         }
2335     }
2336 
2337     private static boolean isAirplaneModeEnabled() {
2338         return runShellCommand("cmd connectivity airplane-mode")
2339                 .trim().equals("enabled");
2340     }
2341 
2342     @Test
2343     public void testGetCaptivePortalServerUrl() {
2344         final String permission = Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q
2345                 ? CONNECTIVITY_INTERNAL
2346                 : NETWORK_SETTINGS;
2347         final String url = runAsShell(permission, mCm::getCaptivePortalServerUrl);
2348         assertNotNull("getCaptivePortalServerUrl must not be null", url);
2349         try {
2350             final URL parsedUrl = new URL(url);
2351             // As per the javadoc, the URL must be HTTP
2352             assertEquals("Invalid captive portal URL protocol", "http", parsedUrl.getProtocol());
2353         } catch (MalformedURLException e) {
2354             throw new AssertionFailedError("Captive portal server URL is invalid: " + e);
2355         }
2356     }
2357 
2358     /**
2359      * Verifies that apps are forbidden from getting ssid information from
2360      * {@Code NetworkCapabilities} if they do not hold NETWORK_SETTINGS permission.
2361      * See b/161370134.
2362      */
2363     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
2364     @Test
2365     public void testSsidInNetworkCapabilities() throws Exception {
2366         assumeTrue("testSsidInNetworkCapabilities cannot execute unless device supports WiFi",
2367                 mPackageManager.hasSystemFeature(FEATURE_WIFI));
2368 
2369         final Network network = mCtsNetUtils.ensureWifiConnected();
2370         final String ssid = unquoteSSID(mWifiManager.getConnectionInfo().getSSID());
2371         assertNotNull("Ssid getting from WifiManager is null", ssid);
2372         // This package should have no NETWORK_SETTINGS permission. Verify that no ssid is contained
2373         // in the NetworkCapabilities.
2374         verifySsidFromQueriedNetworkCapabilities(network, ssid, false /* hasSsid */);
2375         verifySsidFromCallbackNetworkCapabilities(ssid, false /* hasSsid */);
2376         // Adopt shell permission to allow to get ssid information.
2377         runWithShellPermissionIdentity(() -> {
2378             verifySsidFromQueriedNetworkCapabilities(network, ssid, true /* hasSsid */);
2379             verifySsidFromCallbackNetworkCapabilities(ssid, true /* hasSsid */);
2380         });
2381     }
2382 
2383     private void verifySsidFromQueriedNetworkCapabilities(@NonNull Network network,
2384             @NonNull String ssid, boolean hasSsid) throws Exception {
2385         // Verify if ssid is contained in NetworkCapabilities queried from ConnectivityManager.
2386         final NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
2387         assertNotNull("NetworkCapabilities of the network is null", nc);
2388         assertEquals(hasSsid, Pattern.compile(ssid).matcher(nc.toString()).find());
2389     }
2390 
2391     private void verifySsidFromCallbackNetworkCapabilities(@NonNull String ssid, boolean hasSsid)
2392             throws Exception {
2393         final TestableNetworkCallback callback =
2394                 networkCallbackRule.registerNetworkCallback(makeWifiNetworkRequest());
2395         // Registering a callback here guarantees onCapabilitiesChanged is called immediately
2396         // because WiFi network should be connected.
2397         final NetworkCapabilities nc = callback.eventuallyExpect(
2398                 CallbackEntry.NETWORK_CAPS_UPDATED, NETWORK_CALLBACK_TIMEOUT_MS).getCaps();
2399         // Verify if ssid is contained in the NetworkCapabilities received from callback.
2400         assertEquals(hasSsid, Pattern.compile(ssid).matcher(nc.toString()).find());
2401     }
2402 
2403     /**
2404      * Verify background request can only be requested when acquiring
2405      * {@link android.Manifest.permission.NETWORK_SETTINGS}.
2406      */
2407     @AppModeFull(reason = "Instant apps cannot create test networks")
2408     @Test
2409     public void testRequestBackgroundNetwork() {
2410         // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
2411         // shims, and @IgnoreUpTo does not check that.
2412         assumeTrue(TestUtils.shouldTestSApis());
2413 
2414         // Create a tun interface. Use the returned interface name as the specifier to create
2415         // a test network request.
2416         final TestNetworkManager tnm = runWithShellPermissionIdentity(() ->
2417                 mContext.getSystemService(TestNetworkManager.class),
2418                 android.Manifest.permission.MANAGE_TEST_NETWORKS);
2419         final TestNetworkInterface testNetworkInterface = runWithShellPermissionIdentity(() ->
2420                     tnm.createTunInterface(new LinkAddress[]{TEST_LINKADDR}),
2421                 android.Manifest.permission.MANAGE_TEST_NETWORKS,
2422                 android.Manifest.permission.NETWORK_SETTINGS);
2423         assertNotNull(testNetworkInterface);
2424 
2425         final NetworkRequest testRequest = new NetworkRequest.Builder()
2426                 .addTransportType(TRANSPORT_TEST)
2427                 // Test networks do not have NOT_VPN or TRUSTED capabilities by default
2428                 .removeCapability(NET_CAPABILITY_NOT_VPN)
2429                 .removeCapability(NET_CAPABILITY_TRUSTED)
2430                 .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
2431                         testNetworkInterface.getInterfaceName()))
2432                 .build();
2433 
2434         // Verify background network cannot be requested without NETWORK_SETTINGS permission.
2435         final TestableNetworkCallback callback = new TestableNetworkCallback();
2436         final Handler handler = new Handler(Looper.getMainLooper());
2437         assertThrows(SecurityException.class,
2438                 () -> networkCallbackRule.requestBackgroundNetwork(testRequest, callback, handler));
2439 
2440         Network testNetwork = null;
2441         try {
2442             // Request background test network via Shell identity which has NETWORK_SETTINGS
2443             // permission granted.
2444             runWithShellPermissionIdentity(
2445                     () -> networkCallbackRule.requestBackgroundNetwork(
2446                             testRequest, callback, handler),
2447                     new String[] { android.Manifest.permission.NETWORK_SETTINGS });
2448 
2449             // Register the test network agent which has no foreground request associated to it.
2450             // And verify it can satisfy the background network request just fired.
2451             final Binder binder = new Binder();
2452             runWithShellPermissionIdentity(() ->
2453                     tnm.setupTestNetwork(testNetworkInterface.getInterfaceName(), binder),
2454                     new String[] { android.Manifest.permission.MANAGE_TEST_NETWORKS,
2455                             android.Manifest.permission.NETWORK_SETTINGS });
2456             waitForAvailable(callback);
2457             testNetwork = callback.getLastAvailableNetwork();
2458             assertNotNull(testNetwork);
2459 
2460             // The test network that has just connected is a foreground network,
2461             // non-listen requests will get available callback before it can be put into
2462             // background if no foreground request can be satisfied. Thus, wait for a short
2463             // period is needed to let foreground capability go away.
2464             callback.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
2465                     NETWORK_CALLBACK_TIMEOUT_MS,
2466                     c -> c instanceof CallbackEntry.CapabilitiesChanged
2467                             && !((CallbackEntry.CapabilitiesChanged) c).getCaps()
2468                             .hasCapability(NET_CAPABILITY_FOREGROUND));
2469             final NetworkCapabilities nc = mCm.getNetworkCapabilities(testNetwork);
2470             assertFalse("expected background network, but got " + nc,
2471                     nc.hasCapability(NET_CAPABILITY_FOREGROUND));
2472         } finally {
2473             final Network n = testNetwork;
2474             runWithShellPermissionIdentity(() -> {
2475                 if (null != n) {
2476                     tnm.teardownTestNetwork(n);
2477                     callback.eventuallyExpect(CallbackEntry.LOST,
2478                             NETWORK_CALLBACK_TIMEOUT_MS,
2479                             lost -> n.equals(lost.getNetwork()));
2480                 }
2481                 testNetworkInterface.getFileDescriptor().close();
2482             }, new String[] { android.Manifest.permission.MANAGE_TEST_NETWORKS });
2483         }
2484     }
2485 
2486     private class DetailedBlockedStatusCallback extends TestableNetworkCallback {
2487         public void expectAvailableCallbacksWithBlockedReasonNone(Network network) {
2488             super.expectAvailableCallbacks(network, false /* suspended */, true /* validated */,
2489                     BLOCKED_REASON_NONE, NETWORK_CALLBACK_TIMEOUT_MS);
2490         }
2491         public void eventuallyExpectBlockedStatusCallback(Network network, int blockedStatus) {
2492             super.eventuallyExpect(CallbackEntry.BLOCKED_STATUS_INT, NETWORK_CALLBACK_TIMEOUT_MS,
2493                     (it) -> it.getNetwork().equals(network) && it.getReason() == blockedStatus);
2494         }
2495         public void onBlockedStatusChanged(Network network, int blockedReasons) {
2496             Log.v(TAG, "onBlockedStatusChanged " + network + " " + blockedReasons);
2497             getHistory().add(new CallbackEntry.BlockedStatusInt(network, blockedReasons));
2498         }
2499         private void assertNoBlockedStatusCallback() {
2500             super.assertNoCallback(NO_CALLBACK_TIMEOUT_MS,
2501                     c -> c instanceof CallbackEntry.BlockedStatus);
2502         }
2503     }
2504 
2505     private void setRequireVpnForUids(boolean requireVpn, Collection<Range<Integer>> ranges)
2506             throws Exception {
2507         mCmShim.setRequireVpnForUids(requireVpn, ranges);
2508         for (Range<Integer> range : ranges) {
2509             if (requireVpn) {
2510                 mVpnRequiredUidRanges.add(range);
2511             } else {
2512                 assertTrue(mVpnRequiredUidRanges.remove(range));
2513             }
2514         }
2515     }
2516 
2517     // On V+, ConnectivityService generates blockedReasons based on bpf map contents even if the
2518     // otherUid does not exist on device. So if allowlist chain (e.g. background chain) is enabled,
2519     // blockedReasons for otherUid will not be BLOCKED_REASON_NONE.
2520     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
2521     @Test @IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
2522     public void testBlockedStatusCallback() throws Exception {
2523         // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
2524         // shims, and @IgnoreUpTo does not check that.
2525         assumeTrue(TestUtils.shouldTestSApis());
2526         // The test will need a stable active network that is persistent during the test.
2527         // Try to connect to a wifi network and wait for it becomes the default network before
2528         // starting the test to prevent from sudden active network change caused by previous
2529         // executed tests.
2530         if (mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
2531             final Network expectedDefaultNetwork = mCtsNetUtils.ensureWifiConnected();
2532             mCtsNetUtils.expectNetworkIsSystemDefault(expectedDefaultNetwork);
2533         }
2534 
2535         final DetailedBlockedStatusCallback myUidCallback = new DetailedBlockedStatusCallback();
2536         final DetailedBlockedStatusCallback otherUidCallback = new DetailedBlockedStatusCallback();
2537 
2538         final int myUid = Process.myUid();
2539         final int otherUid = UserHandle.getUid(5, Process.FIRST_APPLICATION_UID);
2540         final Handler handler = new Handler(Looper.getMainLooper());
2541 
2542         networkCallbackRule.registerDefaultNetworkCallback(myUidCallback, handler);
2543         runWithShellPermissionIdentity(
2544                 () -> networkCallbackRule.registerDefaultNetworkCallbackForUid(
2545                         otherUid, otherUidCallback, handler), NETWORK_SETTINGS);
2546 
2547         final Network defaultNetwork = myUidCallback.expect(CallbackEntry.AVAILABLE).getNetwork();
2548         final List<DetailedBlockedStatusCallback> allCallbacks =
2549                 List.of(myUidCallback, otherUidCallback);
2550         for (DetailedBlockedStatusCallback callback : allCallbacks) {
2551             callback.eventuallyExpectBlockedStatusCallback(defaultNetwork, BLOCKED_REASON_NONE);
2552         }
2553 
2554         final Range<Integer> myUidRange = new Range<>(myUid, myUid);
2555         final Range<Integer> otherUidRange = new Range<>(otherUid, otherUid);
2556 
2557         runWithShellPermissionIdentity(() -> setRequireVpnForUids(
2558                 true, List.of(myUidRange)), NETWORK_SETTINGS);
2559         myUidCallback.eventuallyExpectBlockedStatusCallback(defaultNetwork,
2560                 BLOCKED_REASON_LOCKDOWN_VPN);
2561         otherUidCallback.assertNoBlockedStatusCallback();
2562 
2563         runWithShellPermissionIdentity(() -> setRequireVpnForUids(
2564                 true, List.of(myUidRange, otherUidRange)), NETWORK_SETTINGS);
2565         myUidCallback.assertNoBlockedStatusCallback();
2566         otherUidCallback.eventuallyExpectBlockedStatusCallback(defaultNetwork,
2567                 BLOCKED_REASON_LOCKDOWN_VPN);
2568 
2569         // setRequireVpnForUids does no deduplication or refcounting. Removing myUidRange does not
2570         // unblock myUid because it was added to the blocked ranges twice.
2571         runWithShellPermissionIdentity(() ->
2572                 setRequireVpnForUids(false, List.of(myUidRange)), NETWORK_SETTINGS);
2573         myUidCallback.assertNoBlockedStatusCallback();
2574         otherUidCallback.assertNoBlockedStatusCallback();
2575 
2576         runWithShellPermissionIdentity(() -> setRequireVpnForUids(
2577                 false, List.of(myUidRange, otherUidRange)), NETWORK_SETTINGS);
2578         myUidCallback.eventuallyExpectBlockedStatusCallback(defaultNetwork, BLOCKED_REASON_NONE);
2579         otherUidCallback.eventuallyExpectBlockedStatusCallback(defaultNetwork, BLOCKED_REASON_NONE);
2580 
2581         myUidCallback.assertNoBlockedStatusCallback();
2582         otherUidCallback.assertNoBlockedStatusCallback();
2583     }
2584 
2585     @Test
2586     public void testSetVpnDefaultForUids() {
2587         assumeTrue(TestUtils.shouldTestUApis());
2588         final String session = UUID.randomUUID().toString();
2589         assertThrows(NullPointerException.class, () -> mCm.setVpnDefaultForUids(session, null));
2590         assertThrows(SecurityException.class,
2591                 () -> mCm.setVpnDefaultForUids(session, new ArraySet<>()));
2592         // For testing the complete behavior of setVpnDefaultForUids(), please refer to
2593         // HostsideVpnTests.
2594     }
2595 
2596     private void doTestLegacyLockdownEnabled() throws Exception {
2597         NetworkInfo info = mCm.getActiveNetworkInfo();
2598         assertNotNull(info);
2599         assertEquals(DetailedState.CONNECTED, info.getDetailedState());
2600 
2601         final TestableNetworkCallback callback;
2602         try {
2603             mCmShim.setLegacyLockdownVpnEnabled(true);
2604 
2605             // setLegacyLockdownVpnEnabled is asynchronous and only takes effect when the
2606             // ConnectivityService handler thread processes it. Ensure it has taken effect by doing
2607             // something that blocks until the handler thread is idle.
2608             callback = networkCallbackRule.registerDefaultNetworkCallback();
2609             waitForAvailable(callback);
2610 
2611             // Test one of the effects of setLegacyLockdownVpnEnabled: the fact that any NetworkInfo
2612             // in state CONNECTED is degraded to CONNECTING if the legacy VPN is not connected.
2613             info = mCm.getActiveNetworkInfo();
2614             assertNotNull(info);
2615             assertEquals(DetailedState.CONNECTING, info.getDetailedState());
2616         } finally {
2617             mCmShim.setLegacyLockdownVpnEnabled(false);
2618         }
2619     }
2620 
2621     @Test
2622     public void testLegacyLockdownEnabled() {
2623         // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
2624         // shims, and @IgnoreUpTo does not check that.
2625         assumeTrue(TestUtils.shouldTestSApis());
2626         runWithShellPermissionIdentity(() -> doTestLegacyLockdownEnabled(), NETWORK_SETTINGS);
2627     }
2628 
2629     @Test
2630     public void testGetCapabilityCarrierName() {
2631         assumeTrue(TestUtils.shouldTestSApis());
2632         assertEquals("ENTERPRISE", NetworkInformationShimImpl.newInstance()
2633                 .getCapabilityCarrierName(ConstantsShim.NET_CAPABILITY_ENTERPRISE));
2634         assertNull(NetworkInformationShimImpl.newInstance()
2635                 .getCapabilityCarrierName(ConstantsShim.NET_CAPABILITY_NOT_VCN_MANAGED));
2636     }
2637 
2638     @Test
2639     public void testSetGlobalProxy() {
2640         assumeTrue(TestUtils.shouldTestSApis());
2641         // Behavior is verified in gts. Verify exception thrown w/o permission.
2642         assertThrows(SecurityException.class, () -> mCm.setGlobalProxy(
2643                 ProxyInfo.buildDirectProxy("example.com" /* host */, 8080 /* port */)));
2644     }
2645 
2646     @Test
2647     public void testFactoryResetWithoutPermission() {
2648         assumeTrue(TestUtils.shouldTestSApis());
2649         assertThrows(SecurityException.class, () -> mCm.factoryReset());
2650     }
2651 
2652     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
2653     @Test
2654     public void testFactoryReset() throws Exception {
2655         assumeTrue(TestUtils.shouldTestSApis());
2656 
2657         // Store current settings.
2658         final int curAvoidBadWifi =
2659                 ConnectivitySettingsManager.getNetworkAvoidBadWifi(mContext);
2660         final int curPrivateDnsMode = ConnectivitySettingsManager.getPrivateDnsMode(mContext);
2661 
2662         final CtsTetheringUtils tetherUtils = new CtsTetheringUtils(mContext);
2663         final TestTetheringEventCallback tetherEventCallback =
2664                 tetherUtils.registerTetheringEventCallback();
2665         try {
2666             tetherEventCallback.assumeWifiTetheringSupported(mContext);
2667             // To prevent WiFi-to-WiFi interruption while entering APM:
2668             //  - If WiFi is retained while entering APM, hotspot will also remain enabled.
2669             //  - If WiFi is off before APM or disabled while entering APM, hotspot will be
2670             //    disabled.
2671             //
2672             // To ensure hotspot always be disabled after enabling APM, disable wifi before
2673             // enabling the hotspot.
2674             mCtsNetUtils.disableWifi();
2675 
2676             tetherUtils.startWifiTethering(tetherEventCallback);
2677             // Update setting to verify the behavior.
2678             setAirplaneMode(true);
2679             // Verify softap lost to make sure airplane mode takes effect. This could
2680             // prevent the race condition between airplane mode enabled and the followed
2681             // up wifi tethering enabled.
2682             tetherEventCallback.expectNoTetheringActive();
2683             tetherUtils.expectSoftApDisabled();
2684 
2685             // start wifi tethering
2686             tetherUtils.startWifiTethering(tetherEventCallback);
2687 
2688             ConnectivitySettingsManager.setPrivateDnsMode(mContext,
2689                     ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF);
2690             ConnectivitySettingsManager.setNetworkAvoidBadWifi(mContext,
2691                     ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI_IGNORE);
2692             assertEquals(AIRPLANE_MODE_ON, Settings.Global.getInt(
2693                     mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON));
2694             // Verify factoryReset
2695             runAsShell(NETWORK_SETTINGS, TETHER_PRIVILEGED, () -> {
2696                 mCm.factoryReset();
2697                 tetherEventCallback.expectNoTetheringActive();
2698             });
2699             verifySettings(AIRPLANE_MODE_OFF,
2700                     ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC,
2701                     ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI_PROMPT);
2702         } finally {
2703             // Restore settings.
2704             setAirplaneMode(false);
2705             ConnectivitySettingsManager.setNetworkAvoidBadWifi(mContext, curAvoidBadWifi);
2706             ConnectivitySettingsManager.setPrivateDnsMode(mContext, curPrivateDnsMode);
2707             tetherUtils.unregisterTetheringEventCallback(tetherEventCallback);
2708             tetherUtils.stopAllTethering();
2709             mCtsNetUtils.ensureWifiConnected();
2710         }
2711     }
2712 
2713     private void setAirplaneMode(boolean enable) {
2714         runAsShell(NETWORK_SETTINGS, () -> mCm.setAirplaneMode(enable));
2715     }
2716 
2717     /**
2718      * Verify that {@link ConnectivityManager#setProfileNetworkPreference} cannot be called
2719      * without required NETWORK_STACK permissions.
2720      */
2721     @Test
2722     public void testSetProfileNetworkPreference_NoPermission() {
2723         // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
2724         // shims, and @IgnoreUpTo does not check that.
2725         assumeTrue(TestUtils.shouldTestSApis());
2726         assertThrows(SecurityException.class, () -> mCm.setProfileNetworkPreference(
2727                 UserHandle.of(0), PROFILE_NETWORK_PREFERENCE_ENTERPRISE, null /* executor */,
2728                 null /* listener */));
2729     }
2730 
2731     @Test
2732     public void testSystemReady() {
2733         assumeTrue(TestUtils.shouldTestSApis());
2734         assertThrows(SecurityException.class, () -> mCm.systemReady());
2735     }
2736 
2737     @Test
2738     public void testGetIpSecNetIdRange() {
2739         assumeTrue(TestUtils.shouldTestSApis());
2740         // The lower refers to ConnectivityManager.TUN_INTF_NETID_START.
2741         final long lower = 64512;
2742         // The upper refers to ConnectivityManager.TUN_INTF_NETID_START
2743         // + ConnectivityManager.TUN_INTF_NETID_RANGE - 1
2744         final long upper = 65535;
2745         assertEquals(lower, (long) ConnectivityManager.getIpSecNetIdRange().getLower());
2746         assertEquals(upper, (long) ConnectivityManager.getIpSecNetIdRange().getUpper());
2747     }
2748 
2749     private void verifySettings(int expectedAirplaneMode, int expectedPrivateDnsMode,
2750             int expectedAvoidBadWifi) throws Exception {
2751         assertEquals(expectedAirplaneMode, Settings.Global.getInt(
2752                 mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON));
2753         assertEquals(expectedPrivateDnsMode,
2754                 ConnectivitySettingsManager.getPrivateDnsMode(mContext));
2755         assertEquals(expectedAvoidBadWifi,
2756                 ConnectivitySettingsManager.getNetworkAvoidBadWifi(mContext));
2757     }
2758 
2759     /**
2760      * Verify that per-app OEM network preference functions as expected for network preference TEST.
2761      * For specified apps, validate networks are prioritized in order: unmetered, TEST transport,
2762      * default network.
2763      */
2764     @AppModeFull(reason = "Instant apps cannot create test networks")
2765     @Test
2766     public void testSetOemNetworkPreferenceForTestPref() throws Exception {
2767         // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
2768         // shims, and @IgnoreUpTo does not check that.
2769         assumeTrue(TestUtils.shouldTestSApis());
2770         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
2771 
2772         final TestNetworkTracker tnt = callWithShellPermissionIdentity(
2773                 () -> initTestNetwork(mContext, TEST_LINKADDR, NETWORK_CALLBACK_TIMEOUT_MS));
2774         final TestableNetworkCallback defaultCallback = new TestableNetworkCallback();
2775         final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback();
2776 
2777         final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
2778         final NetworkCapabilities wifiNetworkCapabilities = callWithShellPermissionIdentity(
2779                 () -> mCm.getNetworkCapabilities(wifiNetwork));
2780         final String ssid = unquoteSSID(wifiNetworkCapabilities.getSsid());
2781         final boolean oldMeteredValue = wifiNetworkCapabilities.isMetered();
2782 
2783         testAndCleanup(() -> {
2784             // This network will be used for unmetered. Wait for it to be validated because
2785             // OEM_NETWORK_PREFERENCE_TEST only prefers NOT_METERED&VALIDATED to a network with
2786             // TRANSPORT_TEST, like OEM_NETWORK_PREFERENCE_OEM_PAID.
2787             setWifiMeteredStatusAndWait(ssid, false /* isMetered */, true /* waitForValidation */);
2788 
2789             setOemNetworkPreferenceForMyPackage(OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST);
2790             registerTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
2791 
2792             // Validate that an unmetered network is used over other networks.
2793             waitForAvailable(defaultCallback, wifiNetwork);
2794             systemDefaultCallback.eventuallyExpect(CallbackEntry.AVAILABLE,
2795                     NETWORK_CALLBACK_TIMEOUT_MS, cb -> wifiNetwork.equals(cb.getNetwork()));
2796 
2797             // Validate that when setting unmetered to metered, unmetered is lost and replaced by
2798             // the network with the TEST transport. Also wait for validation here, in case there
2799             // is a bug that's only visible when the network is validated.
2800             setWifiMeteredStatusAndWait(ssid, true /* isMetered */, true /* waitForValidation */);
2801             defaultCallback.eventuallyExpect(CallbackEntry.LOST, NETWORK_CALLBACK_TIMEOUT_MS,
2802                     l -> l.getNetwork().equals(wifiNetwork));
2803             waitForAvailable(defaultCallback, tnt.getNetwork());
2804             // Depending on if this device has cellular connectivity or not, multiple available
2805             // callbacks may be received. Eventually, metered Wi-Fi should be the final available
2806             // callback in any case therefore confirm its receipt before continuing to assure the
2807             // system is in the expected state.
2808             waitForTransport(systemDefaultCallback, TRANSPORT_WIFI);
2809         }, /* cleanup */ () -> {
2810                 // Validate that removing the test network will fallback to the default network.
2811                 runWithShellPermissionIdentity(tnt::teardown);
2812                 // The other callbacks (LP or NC changes) would receive before LOST callback. Use
2813                 // eventuallyExpect to check callback for avoiding test flake.
2814                 defaultCallback.eventuallyExpect(CallbackEntry.LOST, NETWORK_CALLBACK_TIMEOUT_MS,
2815                         lost -> tnt.getNetwork().equals(lost.getNetwork()));
2816                 waitForAvailable(defaultCallback);
2817             }, /* cleanup */ () -> {
2818                 setWifiMeteredStatusAndWait(ssid, oldMeteredValue, false /* waitForValidation */);
2819             }, /* cleanup */ () -> {
2820                 // Cleanup any prior test state from setOemNetworkPreference
2821                 clearOemNetworkPreference();
2822             });
2823     }
2824 
2825     /**
2826      * Verify that per-app OEM network preference functions as expected for network pref TEST_ONLY.
2827      * For specified apps, validate that only TEST transport type networks are used.
2828      */
2829     @AppModeFull(reason = "Instant apps cannot create test networks")
2830     @Test
2831     public void testSetOemNetworkPreferenceForTestOnlyPref() throws Exception {
2832         // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
2833         // shims, and @IgnoreUpTo does not check that.
2834         assumeTrue(TestUtils.shouldTestSApis());
2835         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
2836 
2837         final TestNetworkTracker tnt = callWithShellPermissionIdentity(
2838                 () -> initTestNetwork(mContext, TEST_LINKADDR, NETWORK_CALLBACK_TIMEOUT_MS));
2839         final TestableNetworkCallback defaultCallback = new TestableNetworkCallback();
2840         final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback();
2841 
2842         final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
2843         final Network testNetwork = tnt.getNetwork();
2844 
2845         testAndCleanup(() -> {
2846             setOemNetworkPreferenceForMyPackage(
2847                     OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY);
2848             registerTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
2849             waitForAvailable(defaultCallback, testNetwork);
2850             systemDefaultCallback.eventuallyExpect(CallbackEntry.AVAILABLE,
2851                     NETWORK_CALLBACK_TIMEOUT_MS, cb -> wifiNetwork.equals(cb.getNetwork()));
2852         }, /* cleanup */ () -> {
2853                 runWithShellPermissionIdentity(tnt::teardown);
2854                 defaultCallback.eventuallyExpect(CallbackEntry.LOST, NETWORK_CALLBACK_TIMEOUT_MS,
2855                         cb -> testNetwork.equals(cb.getNetwork()));
2856 
2857                 // This network preference should only ever use the test network therefore available
2858                 // should not trigger when the test network goes down (e.g. switch to cellular).
2859                 defaultCallback.assertNoCallback();
2860                 // The system default should still be connected to Wi-fi
2861                 assertEquals(wifiNetwork, systemDefaultCallback.getLastAvailableNetwork());
2862             }, /* cleanup */ () -> {
2863                 // Cleanup any prior test state from setOemNetworkPreference
2864                 clearOemNetworkPreference();
2865 
2866                 // The default (non-test) network should be available as the network pref was
2867                 // cleared.
2868                 waitForAvailable(defaultCallback);
2869             });
2870     }
2871 
2872     private void registerTestOemNetworkPreferenceCallbacks(
2873             @NonNull final TestableNetworkCallback defaultCallback,
2874             @NonNull final TestableNetworkCallback systemDefaultCallback) {
2875         networkCallbackRule.registerDefaultNetworkCallback(defaultCallback);
2876         runWithShellPermissionIdentity(() ->
2877                 networkCallbackRule.registerSystemDefaultNetworkCallback(systemDefaultCallback,
2878                         new Handler(Looper.getMainLooper())), NETWORK_SETTINGS);
2879     }
2880 
2881     private static final class OnCompleteListenerCallback {
2882         final CompletableFuture<Object> mDone = new CompletableFuture<>();
2883 
2884         public void onComplete() {
2885             mDone.complete(new Object());
2886         }
2887 
2888         void expectOnComplete() throws Exception {
2889             try {
2890                 mDone.get(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
2891             } catch (TimeoutException e) {
2892                 fail("Expected onComplete() not received after "
2893                         + NETWORK_CALLBACK_TIMEOUT_MS + " ms");
2894             }
2895         }
2896     }
2897 
2898     private void setOemNetworkPreferenceForMyPackage(final int networkPref) throws Exception {
2899         final OemNetworkPreferences pref = new OemNetworkPreferences.Builder()
2900                 .addNetworkPreference(mContext.getPackageName(), networkPref)
2901                 .build();
2902         final OnCompleteListenerCallback oemPrefListener = new OnCompleteListenerCallback();
2903         mUiAutomation.adoptShellPermissionIdentity();
2904         try {
2905             mCm.setOemNetworkPreference(
2906                     pref, mContext.getMainExecutor(), oemPrefListener::onComplete);
2907         } finally {
2908             mUiAutomation.dropShellPermissionIdentity();
2909         }
2910         oemPrefListener.expectOnComplete();
2911     }
2912 
2913     /**
2914      * This will clear the OEM network preference on the device. As there is currently no way of
2915      * getting the existing preference, if this is executed while an existing preference is in
2916      * place, that preference will need to be reapplied after executing this test.
2917      * @throws Exception
2918      */
2919     private void clearOemNetworkPreference() throws Exception {
2920         final OemNetworkPreferences clearPref = new OemNetworkPreferences.Builder().build();
2921         final OnCompleteListenerCallback oemPrefListener = new OnCompleteListenerCallback();
2922         mUiAutomation.adoptShellPermissionIdentity();
2923         try {
2924             mCm.setOemNetworkPreference(
2925                     clearPref, mContext.getMainExecutor(), oemPrefListener::onComplete);
2926         } finally {
2927             mUiAutomation.dropShellPermissionIdentity();
2928         }
2929         oemPrefListener.expectOnComplete();
2930     }
2931 
2932     @Test
2933     public void testSetAcceptPartialConnectivity_NoPermission_GetException() {
2934         assumeTrue(TestUtils.shouldTestSApis());
2935         assertThrows(SecurityException.class, () -> mCm.setAcceptPartialConnectivity(
2936                 mCm.getActiveNetwork(), false /* accept */ , false /* always */));
2937     }
2938 
2939     private void ensureCellIsValidatedBeforeMockingValidationUrls() {
2940         // Verify that current supported network is validated so that the mock http server will not
2941         // apply to unexpected networks. Also see aosp/2208680.
2942         //
2943         // This may also apply to wifi in principle, but in practice methods that mock validation
2944         // URL all disconnect wifi forcefully anyway, so don't wait for wifi to validate.
2945         if (mPackageManager.hasSystemFeature(FEATURE_TELEPHONY)) {
2946             ensureValidatedNetwork(makeCellNetworkRequest());
2947         }
2948     }
2949 
2950     private void ensureValidatedNetwork(NetworkRequest request) {
2951         final TestableNetworkCallback cb = new TestableNetworkCallback();
2952         mCm.registerNetworkCallback(request, cb);
2953         cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
2954                 NETWORK_CALLBACK_TIMEOUT_MS,
2955                 entry -> ((CallbackEntry.CapabilitiesChanged) entry).getCaps()
2956                         .hasCapability(NET_CAPABILITY_VALIDATED));
2957         mCm.unregisterNetworkCallback(cb);
2958     }
2959 
2960     @AppModeFull(reason = "WRITE_DEVICE_CONFIG permission can't be granted to instant apps")
2961     @Test
2962     public void testAcceptPartialConnectivity_validatedNetwork() throws Exception {
2963         assumeTrue(TestUtils.shouldTestSApis());
2964         assumeTrue("testAcceptPartialConnectivity_validatedNetwork cannot execute"
2965                         + " unless device supports WiFi",
2966                 mPackageManager.hasSystemFeature(FEATURE_WIFI));
2967 
2968         try {
2969             // Wait for partial connectivity to be detected on the network
2970             final Network network = preparePartialConnectivity();
2971 
2972             runAsShell(NETWORK_SETTINGS, () -> {
2973                 // The always bit is verified in NetworkAgentTest
2974                 mCm.setAcceptPartialConnectivity(network, true /* accept */, false /* always */);
2975             });
2976 
2977             // Accept partial connectivity network should result in a validated network
2978             expectNetworkHasCapability(network, NET_CAPABILITY_VALIDATED, WIFI_CONNECT_TIMEOUT_MS);
2979         } finally {
2980             mHttpServer.stop();
2981             mTestValidationConfigRule.runAfterNextCleanup(this::reconnectWifi);
2982         }
2983     }
2984 
2985     @AppModeFull(reason = "WRITE_DEVICE_CONFIG permission can't be granted to instant apps")
2986     @Test
2987     public void testRejectPartialConnectivity_TearDownNetwork() throws Exception {
2988         assumeTrue(TestUtils.shouldTestSApis());
2989         assumeTrue("testAcceptPartialConnectivity_validatedNetwork cannot execute"
2990                         + " unless device supports WiFi",
2991                 mPackageManager.hasSystemFeature(FEATURE_WIFI));
2992 
2993         try {
2994             // Wait for partial connectivity to be detected on the network
2995             final Network network = preparePartialConnectivity();
2996 
2997             final TestableNetworkCallback cb = networkCallbackRule.requestNetwork(
2998                     makeWifiNetworkRequest());
2999             runAsShell(NETWORK_SETTINGS, () -> {
3000                 // The always bit is verified in NetworkAgentTest
3001                 mCm.setAcceptPartialConnectivity(network, false /* accept */, false /* always */);
3002             });
3003             // Reject partial connectivity network should cause the network being torn down
3004             assertEquals(network, cb.eventuallyExpect(CallbackEntry.LOST).getNetwork());
3005         } finally {
3006             mHttpServer.stop();
3007             // Wifi will not automatically reconnect to the network. ensureWifiDisconnected cannot
3008             // apply here. Thus, turn off wifi first and restart to restore.
3009             mTestValidationConfigRule.runAfterNextCleanup(() -> {
3010                 mCtsNetUtils.disableWifi();
3011                 mCtsNetUtils.ensureWifiConnected();
3012             });
3013         }
3014     }
3015 
3016     @Test
3017     public void testSetAcceptUnvalidated_NoPermission_GetException() {
3018         assumeTrue(TestUtils.shouldTestSApis());
3019         assertThrows(SecurityException.class, () -> mCm.setAcceptUnvalidated(
3020                 mCm.getActiveNetwork(), false /* accept */ , false /* always */));
3021     }
3022 
3023     @AppModeFull(reason = "WRITE_DEVICE_CONFIG permission can't be granted to instant apps")
3024     @Test
3025     public void testRejectUnvalidated_TearDownNetwork() throws Exception {
3026         assumeTrue(TestUtils.shouldTestSApis());
3027         final boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
3028                 && mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
3029         assumeTrue("testAcceptPartialConnectivity_validatedNetwork cannot execute"
3030                         + " unless device supports WiFi and telephony", canRunTest);
3031 
3032         try {
3033             // Ensure at least one default network candidate connected.
3034             networkCallbackRule.requestCell();
3035 
3036             final Network wifiNetwork = prepareUnvalidatedNetwork();
3037             // Default network should not be wifi ,but checking that wifi is not the default doesn't
3038             // guarantee that it won't become the default in the future.
3039             assertNotEquals(wifiNetwork, mCm.getActiveNetwork());
3040 
3041             final TestableNetworkCallback wifiCb = networkCallbackRule.registerNetworkCallback(
3042                     makeWifiNetworkRequest());
3043             runAsShell(NETWORK_SETTINGS, () -> {
3044                 mCm.setAcceptUnvalidated(wifiNetwork, false /* accept */, false /* always */);
3045             });
3046             waitForLost(wifiCb);
3047         } finally {
3048             mHttpServer.stop();
3049             /// Wifi will not automatically reconnect to the network. ensureWifiDisconnected cannot
3050             // apply here. Thus, turn off wifi first and restart to restore.
3051             mTestValidationConfigRule.runAfterNextCleanup(() -> {
3052                 mCtsNetUtils.disableWifi();
3053                 mCtsNetUtils.ensureWifiConnected();
3054             });
3055         }
3056     }
3057 
3058     @AppModeFull(reason = "WRITE_DEVICE_CONFIG permission can't be granted to instant apps")
3059     @Test
3060     public void testSetAvoidUnvalidated() throws Exception {
3061         assumeTrue(TestUtils.shouldTestSApis());
3062         // TODO: Allow in debuggable ROM only. To be replaced by FabricatedOverlay
3063         assumeTrue(Build.isDebuggable());
3064         final boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
3065                 && mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
3066         assumeTrue("testSetAvoidUnvalidated cannot execute"
3067                 + " unless device supports WiFi and telephony", canRunTest);
3068 
3069         final int previousAvoidBadWifi =
3070                 ConnectivitySettingsManager.getNetworkAvoidBadWifi(mContext);
3071 
3072         allowBadWifi();
3073 
3074         try {
3075             final Network cellNetwork = networkCallbackRule.requestCell();
3076             final Network wifiNetwork = prepareValidatedNetwork();
3077 
3078             final TestableNetworkCallback defaultCb =
3079                     networkCallbackRule.registerDefaultNetworkCallback();
3080             final TestableNetworkCallback wifiCb = networkCallbackRule.registerNetworkCallback(
3081                     makeWifiNetworkRequest());
3082 
3083             // Verify wifi is the default network.
3084             defaultCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
3085                     entry -> wifiNetwork.equals(entry.getNetwork()));
3086             wifiCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
3087                     entry -> wifiNetwork.equals(entry.getNetwork()));
3088             assertTrue(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
3089                     NET_CAPABILITY_VALIDATED));
3090 
3091             // The cell network has already been checked to be validated.
3092             // Configure response code for unvalidated network.
3093             configTestServer(Status.INTERNAL_ERROR, Status.INTERNAL_ERROR);
3094             mCm.reportNetworkConnectivity(wifiNetwork, false);
3095             // Default network should stay on unvalidated wifi because avoid bad wifi is disabled.
3096             defaultCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
3097                     NETWORK_CALLBACK_TIMEOUT_MS,
3098                     entry -> !((CallbackEntry.CapabilitiesChanged) entry).getCaps()
3099                             .hasCapability(NET_CAPABILITY_VALIDATED));
3100             wifiCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
3101                     NETWORK_CALLBACK_TIMEOUT_MS,
3102                     entry -> !((CallbackEntry.CapabilitiesChanged) entry).getCaps()
3103                             .hasCapability(NET_CAPABILITY_VALIDATED));
3104 
3105             runAsShell(NETWORK_SETTINGS, () -> {
3106                 mCm.setAvoidUnvalidated(wifiNetwork);
3107             });
3108             // Default network should be updated to validated cellular network.
3109             defaultCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
3110                     entry -> cellNetwork.equals(entry.getNetwork()));
3111             // The network should not validate again.
3112             wifiCb.assertNoCallback(NO_CALLBACK_TIMEOUT_MS, c -> isValidatedCaps(c));
3113         } finally {
3114             resetAvoidBadWifi(previousAvoidBadWifi);
3115             mHttpServer.stop();
3116             mTestValidationConfigRule.runAfterNextCleanup(this::reconnectWifi);
3117         }
3118     }
3119 
3120     private boolean isValidatedCaps(CallbackEntry c) {
3121         if (!(c instanceof CallbackEntry.CapabilitiesChanged)) return false;
3122         final CallbackEntry.CapabilitiesChanged capsChanged = (CallbackEntry.CapabilitiesChanged) c;
3123         return capsChanged.getCaps().hasCapability(NET_CAPABILITY_VALIDATED);
3124     }
3125 
3126     private void resetAvoidBadWifi(int settingValue) {
3127         setTestAllowBadWifiResource(0 /* timeMs */);
3128         ConnectivitySettingsManager.setNetworkAvoidBadWifi(mContext, settingValue);
3129     }
3130 
3131     private void allowBadWifi() {
3132         setTestAllowBadWifiResource(
3133                 System.currentTimeMillis() + WIFI_CONNECT_TIMEOUT_MS /* timeMs */);
3134         ConnectivitySettingsManager.setNetworkAvoidBadWifi(mContext,
3135                 ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI_IGNORE);
3136     }
3137 
3138     private void setTestAllowBadWifiResource(long timeMs) {
3139         runAsShell(NETWORK_SETTINGS, () -> {
3140             mCm.setTestAllowBadWifiUntil(timeMs);
3141         });
3142     }
3143 
3144     private Network expectNetworkHasCapability(Network network, int expectedNetCap, long timeout) {
3145         return networkCallbackRule.registerNetworkCallback(new NetworkRequest.Builder().build())
3146                 .eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, timeout,
3147                         cb -> cb.getNetwork().equals(network)
3148                                 && cb.getCaps().hasCapability(expectedNetCap)).getNetwork();
3149     }
3150 
3151     private void prepareHttpServer() throws Exception {
3152         runAsShell(READ_DEVICE_CONFIG, () -> {
3153             // Verify that the test URLs are not normally set on the device, but do not fail if the
3154             // test URLs are set to what this test uses (URLs on localhost), in case the test was
3155             // interrupted manually and rerun.
3156             assertEmptyOrLocalhostUrl(TEST_CAPTIVE_PORTAL_HTTPS_URL);
3157             assertEmptyOrLocalhostUrl(TEST_CAPTIVE_PORTAL_HTTP_URL);
3158         });
3159 
3160         NetworkValidationTestUtil.clearValidationTestUrlsDeviceConfig();
3161 
3162         mHttpServer.start();
3163     }
3164 
3165     private Network reconnectWifi() {
3166         mCtsNetUtils.ensureWifiDisconnected(null /* wifiNetworkToCheck */);
3167         return mCtsNetUtils.ensureWifiConnected();
3168     }
3169 
3170     private Network prepareValidatedNetwork() throws Exception {
3171         ensureCellIsValidatedBeforeMockingValidationUrls();
3172 
3173         prepareHttpServer();
3174         configTestServer(Status.NO_CONTENT, Status.NO_CONTENT);
3175         // Disconnect wifi first then start wifi network with configuration.
3176         final Network wifiNetwork = reconnectWifi();
3177 
3178         return expectNetworkHasCapability(wifiNetwork, NET_CAPABILITY_VALIDATED,
3179                 WIFI_CONNECT_TIMEOUT_MS);
3180     }
3181 
3182     private Network preparePartialConnectivity() throws Exception {
3183         ensureCellIsValidatedBeforeMockingValidationUrls();
3184 
3185         prepareHttpServer();
3186         // Configure response code for partial connectivity
3187         configTestServer(Status.INTERNAL_ERROR  /* httpsStatusCode */,
3188                 Status.NO_CONTENT  /* httpStatusCode */);
3189         // Disconnect wifi first then start wifi network with configuration.
3190         mCtsNetUtils.ensureWifiDisconnected(null /* wifiNetworkToCheck */);
3191         final Network network = mCtsNetUtils.ensureWifiConnected();
3192 
3193         return expectNetworkHasCapability(network, NET_CAPABILITY_PARTIAL_CONNECTIVITY,
3194                 WIFI_CONNECT_TIMEOUT_MS);
3195     }
3196 
3197     private Network prepareUnvalidatedNetwork() throws Exception {
3198         ensureCellIsValidatedBeforeMockingValidationUrls();
3199 
3200         prepareHttpServer();
3201         // Configure response code for unvalidated network
3202         configTestServer(Status.INTERNAL_ERROR /* httpsStatusCode */,
3203                 Status.INTERNAL_ERROR /* httpStatusCode */);
3204 
3205         // Disconnect wifi first then start wifi network with configuration.
3206         mCtsNetUtils.ensureWifiDisconnected(null /* wifiNetworkToCheck */);
3207         final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
3208         return expectNetworkHasCapability(wifiNetwork, NET_CAPABILITY_INTERNET,
3209                 WIFI_CONNECT_TIMEOUT_MS);
3210     }
3211 
3212     private String makeUrl(String path) {
3213         return "http://localhost:" + mHttpServer.getListeningPort() + path;
3214     }
3215 
3216     private void assertEmptyOrLocalhostUrl(String urlKey) {
3217         final String url = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, urlKey);
3218         assertTrue(urlKey + " must not be set in production scenarios, current value= " + url,
3219                 TextUtils.isEmpty(url) || LOCALHOST_HOSTNAME.equals(Uri.parse(url).getHost()));
3220     }
3221 
3222     private void configTestServer(IStatus httpsStatusCode, IStatus httpStatusCode) {
3223         mHttpServer.addResponse(new TestHttpServer.Request(
3224                 TEST_HTTPS_URL_PATH, Method.GET, "" /* queryParameters */),
3225                 httpsStatusCode, null /* locationHeader */, "" /* content */);
3226         mHttpServer.addResponse(new TestHttpServer.Request(
3227                 TEST_HTTP_URL_PATH, Method.GET, "" /* queryParameters */),
3228                 httpStatusCode, null /* locationHeader */, "" /* content */);
3229         NetworkValidationTestUtil.setHttpsUrlDeviceConfig(mTestValidationConfigRule,
3230                 makeUrl(TEST_HTTPS_URL_PATH));
3231         NetworkValidationTestUtil.setHttpUrlDeviceConfig(mTestValidationConfigRule,
3232                 makeUrl(TEST_HTTP_URL_PATH));
3233         NetworkValidationTestUtil.setUrlExpirationDeviceConfig(mTestValidationConfigRule,
3234                 System.currentTimeMillis() + WIFI_CONNECT_TIMEOUT_MS);
3235     }
3236 
3237     @AppModeFull(reason = "Need WiFi support to test the default active network")
3238     // NetworkActivityTracker is not mainlined before S.
3239     @Test @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
3240     public void testDefaultNetworkActiveListener() throws Exception {
3241         final boolean supportWifi = mPackageManager.hasSystemFeature(FEATURE_WIFI);
3242         final boolean supportTelephony = mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
3243         assumeTrue("testDefaultNetworkActiveListener cannot execute"
3244                 + " unless device supports WiFi or telephony", (supportWifi || supportTelephony));
3245 
3246         if (supportWifi) {
3247             mCtsNetUtils.ensureWifiDisconnected(null /* wifiNetworkToCheck */);
3248         }
3249 
3250         final CompletableFuture<Boolean> future = new CompletableFuture<>();
3251         final ConnectivityManager.OnNetworkActiveListener listener = () -> future.complete(true);
3252         mCm.addDefaultNetworkActiveListener(listener);
3253         testAndCleanup(() -> {
3254             // New default network connected will trigger a network activity notification.
3255             if (supportWifi) {
3256                 mCtsNetUtils.ensureWifiConnected();
3257             } else {
3258                 networkCallbackRule.requestCell();
3259             }
3260             assertTrue(future.get(LISTEN_ACTIVITY_TIMEOUT_MS, TimeUnit.MILLISECONDS));
3261         }, () -> {
3262                 mCm.removeDefaultNetworkActiveListener(listener);
3263             });
3264     }
3265 
3266     /**
3267      *  The networks used in this test are real networks and as such they can see seemingly random
3268      *  updates of their capabilities or link properties as conditions change, e.g. the network
3269      *  loses validation or IPv4 shows up. Many tests should simply treat these callbacks as
3270      *  spurious.
3271      */
3272     private void assertNoCallbackExceptCapOrLpChange(
3273             @NonNull final TestableNetworkCallback cb) {
3274         cb.assertNoCallback(NO_CALLBACK_TIMEOUT_MS,
3275                 c -> !(c instanceof CallbackEntry.CapabilitiesChanged
3276                         || c instanceof CallbackEntry.LinkPropertiesChanged));
3277     }
3278 
3279     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
3280     @Test
3281     public void testMobileDataPreferredUids() throws Exception {
3282         assumeTrue(TestUtils.shouldTestSApis());
3283         final boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
3284                 && mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
3285         assumeTrue("testMobileDataPreferredUidsWithCallback cannot execute"
3286                 + " unless device supports both WiFi and telephony", canRunTest);
3287 
3288         final int uid = mPackageManager.getPackageUid(mContext.getPackageName(), 0 /* flag */);
3289         final Set<Integer> mobileDataPreferredUids =
3290                 ConnectivitySettingsManager.getMobileDataPreferredUids(mContext);
3291         // CtsNetTestCases uid should not list in MOBILE_DATA_PREFERRED_UIDS setting because it just
3292         // installs to device. In case the uid is existed in setting mistakenly, try to remove the
3293         // uid and set correct uids to setting.
3294         mobileDataPreferredUids.remove(uid);
3295         ConnectivitySettingsManager.setMobileDataPreferredUids(mContext, mobileDataPreferredUids);
3296 
3297         // For testing mobile data preferred uids feature, it needs both wifi and cell network.
3298         final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
3299         final Network cellNetwork = networkCallbackRule.requestCell();
3300         final Handler h = new Handler(Looper.getMainLooper());
3301         final TestableNetworkCallback systemDefaultCb = runWithShellPermissionIdentity(
3302                 () -> networkCallbackRule.registerSystemDefaultNetworkCallback(h),
3303                 NETWORK_SETTINGS);
3304 
3305         final TestableNetworkCallback defaultTrackingCb =
3306                 networkCallbackRule.registerDefaultNetworkCallback();
3307 
3308         try {
3309             // CtsNetTestCases uid is not listed in MOBILE_DATA_PREFERRED_UIDS setting, so the
3310             // per-app default network should be same as system default network.
3311             waitForAvailable(systemDefaultCb, wifiNetwork);
3312             waitForAvailable(defaultTrackingCb, wifiNetwork);
3313             // Active network for CtsNetTestCases uid should be wifi now.
3314             assertEquals(wifiNetwork, mCm.getActiveNetwork());
3315 
3316             // Add CtsNetTestCases uid to MOBILE_DATA_PREFERRED_UIDS setting, then available per-app
3317             // default network callback should be received with cell network.
3318             final Set<Integer> newMobileDataPreferredUids = new ArraySet<>(mobileDataPreferredUids);
3319             newMobileDataPreferredUids.add(uid);
3320             ConnectivitySettingsManager.setMobileDataPreferredUids(
3321                     mContext, newMobileDataPreferredUids);
3322             defaultTrackingCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
3323                     entry -> cellNetwork.equals(entry.getNetwork()));
3324             // No change for system default network. Expect no callback except CapabilitiesChanged
3325             // or LinkPropertiesChanged which may be triggered randomly from wifi network.
3326             assertNoCallbackExceptCapOrLpChange(systemDefaultCb);
3327             // Active network for CtsNetTestCases uid should change to cell, too.
3328             assertEquals(cellNetwork, mCm.getActiveNetwork());
3329 
3330             // Remove CtsNetTestCases uid from MOBILE_DATA_PREFERRED_UIDS setting, then available
3331             // per-app default network callback should be received again with system default network
3332             newMobileDataPreferredUids.remove(uid);
3333             ConnectivitySettingsManager.setMobileDataPreferredUids(
3334                     mContext, newMobileDataPreferredUids);
3335             defaultTrackingCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
3336                     entry -> wifiNetwork.equals(entry.getNetwork()));
3337             // No change for system default network. Expect no callback except CapabilitiesChanged
3338             // or LinkPropertiesChanged which may be triggered randomly from wifi network.
3339             assertNoCallbackExceptCapOrLpChange(systemDefaultCb);
3340             // Active network for CtsNetTestCases uid should change back to wifi.
3341             assertEquals(wifiNetwork, mCm.getActiveNetwork());
3342         } finally {
3343             // Restore setting.
3344             ConnectivitySettingsManager.setMobileDataPreferredUids(
3345                     mContext, mobileDataPreferredUids);
3346         }
3347     }
3348 
3349     private void assertBindSocketToNetworkSuccess(final Network network) throws Exception {
3350         final CompletableFuture<Boolean> future = new CompletableFuture<>();
3351         final ExecutorService executor = Executors.newSingleThreadExecutor();
3352         try {
3353             executor.execute(() -> {
3354                 for (int i = 0; i < 300; i++) {
3355                     SystemClock.sleep(10);
3356 
3357                     try (Socket socket = new Socket()) {
3358                         network.bindSocket(socket);
3359                         future.complete(true);
3360                         return;
3361                     } catch (IOException e) { }
3362                 }
3363             });
3364             assertTrue(future.get(APPLYING_UIDS_ALLOWED_ON_RESTRICTED_NETWORKS_TIMEOUT_MS,
3365                     TimeUnit.MILLISECONDS));
3366         } finally {
3367             executor.shutdown();
3368         }
3369     }
3370 
3371     private static NetworkAgent createRestrictedNetworkAgent(final Context context) {
3372         // Create test network agent with restricted network.
3373         final NetworkCapabilities nc = new NetworkCapabilities.Builder()
3374                 .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
3375                 .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
3376                 .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
3377                         TEST_RESTRICTED_NW_IFACE_NAME))
3378                 .build();
3379         final NetworkAgent agent = new NetworkAgent(context, Looper.getMainLooper(), TAG, nc,
3380                 new LinkProperties(), 10 /* score */, new NetworkAgentConfig.Builder().build(),
3381                 new NetworkProvider(context, Looper.getMainLooper(), TAG)) {};
3382         runWithShellPermissionIdentity(() -> agent.register(),
3383                 android.Manifest.permission.MANAGE_TEST_NETWORKS);
3384         agent.markConnected();
3385 
3386         return agent;
3387     }
3388 
3389     @AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
3390     @Test
3391     public void testUidsAllowedOnRestrictedNetworks() throws Exception {
3392         assumeTestSApis();
3393 
3394         // TODO (b/175199465): figure out a reasonable permission check for
3395         //  setUidsAllowedOnRestrictedNetworks that allows tests but not system-external callers.
3396         assumeTrue(Build.isDebuggable());
3397 
3398         final int uid = mPackageManager.getPackageUid(mContext.getPackageName(), 0 /* flag */);
3399         final Set<Integer> originalUidsAllowedOnRestrictedNetworks =
3400                 ConnectivitySettingsManager.getUidsAllowedOnRestrictedNetworks(mContext);
3401         // CtsNetTestCases uid should not list in UIDS_ALLOWED_ON_RESTRICTED_NETWORKS setting
3402         // because it has been just installed to device. In case the uid is existed in setting
3403         // mistakenly, try to remove the uid and set correct uids to setting.
3404         originalUidsAllowedOnRestrictedNetworks.remove(uid);
3405         runWithShellPermissionIdentity(() -> setUidsAllowedOnRestrictedNetworks(
3406                 mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
3407 
3408         // File a restricted network request with permission first to hold the connection.
3409         final NetworkRequest testRequest = new NetworkRequest.Builder()
3410                 .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
3411                 .removeCapability(NET_CAPABILITY_TRUSTED)
3412                 .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
3413                 .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
3414                         TEST_RESTRICTED_NW_IFACE_NAME))
3415                 .build();
3416         final TestableNetworkCallback testNetworkCb = runWithShellPermissionIdentity(
3417                 () -> networkCallbackRule.requestNetwork(testRequest),
3418                 CONNECTIVITY_USE_RESTRICTED_NETWORKS);
3419 
3420         // File another restricted network request without permission.
3421         final TestableNetworkCallback restrictedNetworkCb = new TestableNetworkCallback();
3422         final NetworkRequest restrictedRequest = new NetworkRequest.Builder()
3423                 .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
3424                 .removeCapability(NET_CAPABILITY_TRUSTED)
3425                 .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
3426                 .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
3427                         TEST_RESTRICTED_NW_IFACE_NAME))
3428                 .build();
3429         // Uid is not in allowed list and no permissions. Expect that SecurityException will throw.
3430         assertThrows(SecurityException.class,
3431                 () -> mCm.requestNetwork(restrictedRequest, restrictedNetworkCb));
3432 
3433         final NetworkAgent agent = createRestrictedNetworkAgent(mContext);
3434         final Network network = agent.getNetwork();
3435 
3436         try (Socket socket = new Socket()) {
3437             // Verify that the network is restricted.
3438             testNetworkCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
3439                     NETWORK_CALLBACK_TIMEOUT_MS,
3440                     entry -> network.equals(entry.getNetwork())
3441                             && (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
3442                             .hasCapability(NET_CAPABILITY_NOT_RESTRICTED)));
3443             // CtsNetTestCases package doesn't hold CONNECTIVITY_USE_RESTRICTED_NETWORKS, so it
3444             // does not allow to bind socket to restricted network.
3445             assertThrows(IOException.class, () -> network.bindSocket(socket));
3446 
3447             // Add CtsNetTestCases uid to UIDS_ALLOWED_ON_RESTRICTED_NETWORKS setting, then it can
3448             // bind socket to restricted network normally.
3449             final Set<Integer> newUidsAllowedOnRestrictedNetworks =
3450                     new ArraySet<>(originalUidsAllowedOnRestrictedNetworks);
3451             newUidsAllowedOnRestrictedNetworks.add(uid);
3452             runWithShellPermissionIdentity(() -> setUidsAllowedOnRestrictedNetworks(
3453                     mContext, newUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
3454             // Wait a while for sending allowed uids on the restricted network to netd.
3455             // TODD: Have a significant signal to know the uids has been sent to netd.
3456             assertBindSocketToNetworkSuccess(network);
3457 
3458             if (TestUtils.shouldTestTApis()) {
3459                 // Uid is in allowed list. Try file network request again.
3460                 networkCallbackRule.requestNetwork(restrictedRequest, restrictedNetworkCb);
3461                 // Verify that the network is restricted.
3462                 restrictedNetworkCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
3463                         NETWORK_CALLBACK_TIMEOUT_MS,
3464                         entry -> network.equals(entry.getNetwork())
3465                                 && (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
3466                                 .hasCapability(NET_CAPABILITY_NOT_RESTRICTED)));
3467             }
3468         } finally {
3469             agent.unregister();
3470 
3471             // Restore setting.
3472             runWithShellPermissionIdentity(() -> setUidsAllowedOnRestrictedNetworks(
3473                     mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
3474         }
3475     }
3476 
3477     @Test
3478     public void testDump() throws Exception {
3479         final String dumpOutput = DumpTestUtils.dumpServiceWithShellPermission(
3480                 Context.CONNECTIVITY_SERVICE, "--short");
3481         assertTrue(dumpOutput, dumpOutput.contains("Active default network"));
3482     }
3483 
3484     @Test @IgnoreUpTo(SC_V2)
3485     public void testDumpBpfNetMaps() throws Exception {
3486         final String[] args = new String[] {"--short", "trafficcontroller"};
3487         String dumpOutput = DumpTestUtils.dumpServiceWithShellPermission(
3488                 Context.CONNECTIVITY_SERVICE, args);
3489         assertTrue(dumpOutput, dumpOutput.contains("TrafficController"));
3490         assertFalse(dumpOutput, dumpOutput.contains("BPF map content"));
3491 
3492         dumpOutput = DumpTestUtils.dumpServiceWithShellPermission(
3493                 Context.CONNECTIVITY_SERVICE, args[1]);
3494         assertTrue(dumpOutput, dumpOutput.contains("BPF map content"));
3495     }
3496 
3497     private void checkFirewallBlocking(final DatagramSocket srcSock, final DatagramSocket dstSock,
3498             final boolean expectBlock, final int chain) throws Exception {
3499         final int uid = Process.myUid();
3500         final Random random = new Random();
3501         final byte[] sendData = new byte[100];
3502         random.nextBytes(sendData);
3503 
3504         final DatagramPacket pkt = new DatagramPacket(sendData, sendData.length,
3505                 InetAddresses.parseNumericAddress("::1"), dstSock.getLocalPort());
3506         try {
3507             srcSock.send(pkt);
3508         } catch (IOException e) {
3509             if (expectBlock) {
3510                 return;
3511             }
3512             fail("Expect not to be blocked by firewall but sending packet was blocked:"
3513                     + " chain=" + chain
3514                     + " chainEnabled=" + mCm.getFirewallChainEnabled(chain)
3515                     + " uid=" + uid
3516                     + " uidFirewallRule=" + mCm.getUidFirewallRule(chain, uid));
3517         }
3518 
3519         dstSock.receive(pkt);
3520         assertArrayEquals(sendData, pkt.getData());
3521 
3522         if (expectBlock) {
3523             fail("Expect to be blocked by firewall but sending packet was not blocked:"
3524                     + " chain=" + chain
3525                     + " chainEnabled=" + mCm.getFirewallChainEnabled(chain)
3526                     + " uid=" + uid
3527                     + " uidFirewallRule=" + mCm.getUidFirewallRule(chain, uid));
3528         }
3529     }
3530 
3531     private static final boolean EXPECT_PASS = false;
3532     private static final boolean EXPECT_BLOCK = true;
3533 
3534     // ALLOWLIST means the firewall denies all by default, uids must be explicitly allowed
3535     // DENYLIST means the firewall allows all by default, uids must be explicitly denyed
3536     private static final boolean ALLOWLIST = true;
3537     private static final boolean DENYLIST = false;
3538 
3539     private void doTestFirewallBlocking(final int chain, final boolean isAllowList) {
3540         final int myUid = Process.myUid();
3541         final int ruleToAddMatch = isAllowList ? FIREWALL_RULE_ALLOW : FIREWALL_RULE_DENY;
3542         final int ruleToRemoveMatch = isAllowList ? FIREWALL_RULE_DENY : FIREWALL_RULE_ALLOW;
3543 
3544         runWithShellPermissionIdentity(() -> {
3545             // Firewall chain status will be restored after the test.
3546             final boolean wasChainEnabled = mCm.getFirewallChainEnabled(chain);
3547             final int previousUidFirewallRule = mCm.getUidFirewallRule(chain, myUid);
3548             final DatagramSocket srcSock = new DatagramSocket();
3549             final DatagramSocket dstSock = new DatagramSocket();
3550             testAndCleanup(() -> {
3551                 if (wasChainEnabled) {
3552                     mCm.setFirewallChainEnabled(chain, false /* enable */);
3553                 }
3554                 if (previousUidFirewallRule == ruleToAddMatch) {
3555                     mCm.setUidFirewallRule(chain, myUid, ruleToRemoveMatch);
3556                 }
3557                 dstSock.setSoTimeout(SOCKET_TIMEOUT_MS);
3558 
3559                 // Chain disabled, UID not on chain.
3560                 checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS, chain);
3561 
3562                 // Chain enabled, UID not on chain.
3563                 mCm.setFirewallChainEnabled(chain, true /* enable */);
3564                 assertTrue(mCm.getFirewallChainEnabled(chain));
3565                 checkFirewallBlocking(
3566                         srcSock, dstSock, isAllowList ? EXPECT_BLOCK : EXPECT_PASS, chain);
3567 
3568                 // Chain enabled, UID on chain.
3569                 mCm.setUidFirewallRule(chain, myUid, ruleToAddMatch);
3570                 checkFirewallBlocking(
3571                         srcSock, dstSock, isAllowList ?  EXPECT_PASS : EXPECT_BLOCK, chain);
3572 
3573                 // Chain disabled, UID on chain.
3574                 mCm.setFirewallChainEnabled(chain, false /* enable */);
3575                 assertFalse(mCm.getFirewallChainEnabled(chain));
3576                 checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS, chain);
3577 
3578                 // Chain disabled, UID not on chain.
3579                 mCm.setUidFirewallRule(chain, myUid, ruleToRemoveMatch);
3580                 checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS, chain);
3581             }, /* cleanup */ () -> {
3582                     srcSock.close();
3583                     dstSock.close();
3584                 }, /* cleanup */ () -> {
3585                     // Restore the global chain status
3586                     mCm.setFirewallChainEnabled(chain, wasChainEnabled);
3587                 }, /* cleanup */ () -> {
3588                     // Restore the uid firewall rule status
3589                     try {
3590                         mCm.setUidFirewallRule(chain, myUid, previousUidFirewallRule);
3591                     } catch (IllegalStateException ignored) {
3592                         // Removing match causes an exception when the rule entry for the uid does
3593                         // not exist. But this is fine and can be ignored.
3594                     }
3595                 });
3596         }, NETWORK_SETTINGS);
3597     }
3598 
3599     @Test @IgnoreUpTo(SC_V2) @ConnectivityModuleTest
3600     @AppModeFull(reason = "Socket cannot bind in instant app mode")
3601     public void testFirewallBlockingDozable() {
3602         doTestFirewallBlocking(FIREWALL_CHAIN_DOZABLE, ALLOWLIST);
3603     }
3604 
3605     // Disable test - needs to be fixed
3606     @Ignore
3607     @Test @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @ConnectivityModuleTest
3608     @AppModeFull(reason = "Socket cannot bind in instant app mode")
3609     public void testFirewallBlockingBackground() {
3610         doTestFirewallBlocking(FIREWALL_CHAIN_BACKGROUND, ALLOWLIST);
3611     }
3612 
3613     @Test @IgnoreUpTo(SC_V2) @ConnectivityModuleTest
3614     @AppModeFull(reason = "Socket cannot bind in instant app mode")
3615     public void testFirewallBlockingPowersave() {
3616         doTestFirewallBlocking(FIREWALL_CHAIN_POWERSAVE, ALLOWLIST);
3617     }
3618 
3619     @Test @IgnoreUpTo(SC_V2) @ConnectivityModuleTest
3620     @AppModeFull(reason = "Socket cannot bind in instant app mode")
3621     public void testFirewallBlockingRestricted() {
3622         doTestFirewallBlocking(FIREWALL_CHAIN_RESTRICTED, ALLOWLIST);
3623     }
3624 
3625     @Test @IgnoreUpTo(SC_V2) @ConnectivityModuleTest
3626     @AppModeFull(reason = "Socket cannot bind in instant app mode")
3627     public void testFirewallBlockingLowPowerStandby() {
3628         doTestFirewallBlocking(FIREWALL_CHAIN_LOW_POWER_STANDBY, ALLOWLIST);
3629     }
3630 
3631     @Test @IgnoreUpTo(SC_V2) @ConnectivityModuleTest
3632     @AppModeFull(reason = "Socket cannot bind in instant app mode")
3633     public void testFirewallBlockingStandby() {
3634         doTestFirewallBlocking(FIREWALL_CHAIN_STANDBY, DENYLIST);
3635     }
3636 
3637     @Test @IgnoreUpTo(SC_V2) @ConnectivityModuleTest
3638     @AppModeFull(reason = "Socket cannot bind in instant app mode")
3639     public void testFirewallBlockingOemDeny1() {
3640         doTestFirewallBlocking(FIREWALL_CHAIN_OEM_DENY_1, DENYLIST);
3641     }
3642 
3643     @Test @IgnoreUpTo(SC_V2) @ConnectivityModuleTest
3644     @AppModeFull(reason = "Socket cannot bind in instant app mode")
3645     public void testFirewallBlockingOemDeny2() {
3646         doTestFirewallBlocking(FIREWALL_CHAIN_OEM_DENY_2, DENYLIST);
3647     }
3648 
3649     @Test @IgnoreUpTo(SC_V2) @ConnectivityModuleTest
3650     @AppModeFull(reason = "Socket cannot bind in instant app mode")
3651     public void testFirewallBlockingOemDeny3() {
3652         doTestFirewallBlocking(FIREWALL_CHAIN_OEM_DENY_3, DENYLIST);
3653     }
3654 
3655     private void assertSocketOpen(final Socket socket) throws Exception {
3656         mCtsNetUtils.testHttpRequest(socket);
3657     }
3658 
3659     private void assertSocketClosed(final Socket socket) throws Exception {
3660         try {
3661             mCtsNetUtils.testHttpRequest(socket);
3662             fail("Socket is expected to be closed");
3663         } catch (SocketException expected) {
3664         }
3665     }
3666 
3667     private void setUidFirewallRule(final int chain, final int uid, final int rule) {
3668         try {
3669             mCm.setUidFirewallRule(chain, uid, rule);
3670         } catch (IllegalStateException ignored) {
3671             // Removing match causes an exception when the rule entry for the uid does
3672             // not exist. But this is fine and can be ignored.
3673         }
3674     }
3675 
3676     private static final boolean EXPECT_OPEN = false;
3677     private static final boolean EXPECT_CLOSE = true;
3678 
3679     private void doTestFirewallCloseSocket(final int chain, final int rule, final int targetUid,
3680             final boolean expectClose) {
3681         runWithShellPermissionIdentity(() -> {
3682             // Firewall chain status will be restored after the test.
3683             final boolean wasChainEnabled = mCm.getFirewallChainEnabled(chain);
3684             final int myUid = Process.myUid();
3685             final int previousMyUidFirewallRule = mCm.getUidFirewallRule(chain, myUid);
3686             final int previousUidFirewallRule = mCm.getUidFirewallRule(chain, targetUid);
3687             final Socket socket = new Socket(TEST_HOST, HTTP_PORT);
3688             socket.setSoTimeout(NETWORK_REQUEST_TIMEOUT_MS);
3689             testAndCleanup(() -> {
3690                 mCm.setFirewallChainEnabled(chain, false /* enable */);
3691                 assertSocketOpen(socket);
3692 
3693                 setUidFirewallRule(chain, targetUid, rule);
3694                 if (targetUid != myUid) {
3695                     // If this test does not set rule on myUid, remove existing rule on myUid
3696                     setUidFirewallRule(chain, myUid, FIREWALL_RULE_DEFAULT);
3697                 }
3698 
3699                 mCm.setFirewallChainEnabled(chain, true /* enable */);
3700 
3701                 if (expectClose) {
3702                     assertSocketClosed(socket);
3703                 } else {
3704                     assertSocketOpen(socket);
3705                 }
3706             }, /* cleanup */ () -> {
3707                     // Restore the global chain status
3708                     mCm.setFirewallChainEnabled(chain, wasChainEnabled);
3709                 }, /* cleanup */ () -> {
3710                     // Restore the uid firewall rule status
3711                     setUidFirewallRule(chain, targetUid, previousUidFirewallRule);
3712                     if (targetUid != myUid) {
3713                         setUidFirewallRule(chain, myUid, previousMyUidFirewallRule);
3714                     }
3715                 }, /* cleanup */ () -> {
3716                     socket.close();
3717                 });
3718         }, NETWORK_SETTINGS);
3719     }
3720 
3721     @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) @ConnectivityModuleTest
3722     public void testFirewallCloseSocketAllowlistChainAllow() {
3723         doTestFirewallCloseSocket(FIREWALL_CHAIN_DOZABLE, FIREWALL_RULE_ALLOW,
3724                 Process.myUid(), EXPECT_OPEN);
3725     }
3726 
3727     @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) @ConnectivityModuleTest
3728     public void testFirewallCloseSocketAllowlistChainDeny() {
3729         doTestFirewallCloseSocket(FIREWALL_CHAIN_DOZABLE, FIREWALL_RULE_DENY,
3730                 Process.myUid(), EXPECT_CLOSE);
3731     }
3732 
3733     @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) @ConnectivityModuleTest
3734     public void testFirewallCloseSocketAllowlistChainOtherUid() {
3735         doTestFirewallCloseSocket(FIREWALL_CHAIN_DOZABLE, FIREWALL_RULE_ALLOW,
3736                 Process.myUid() + 1, EXPECT_CLOSE);
3737         doTestFirewallCloseSocket(FIREWALL_CHAIN_DOZABLE, FIREWALL_RULE_DENY,
3738                 Process.myUid() + 1, EXPECT_CLOSE);
3739     }
3740 
3741     @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) @ConnectivityModuleTest
3742     public void testFirewallCloseSocketDenylistChainAllow() {
3743         doTestFirewallCloseSocket(FIREWALL_CHAIN_STANDBY, FIREWALL_RULE_ALLOW,
3744                 Process.myUid(), EXPECT_OPEN);
3745     }
3746 
3747     @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) @ConnectivityModuleTest
3748     public void testFirewallCloseSocketDenylistChainDeny() {
3749         doTestFirewallCloseSocket(FIREWALL_CHAIN_STANDBY, FIREWALL_RULE_DENY,
3750                 Process.myUid(), EXPECT_CLOSE);
3751     }
3752 
3753     @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) @ConnectivityModuleTest
3754     public void testFirewallCloseSocketDenylistChainOtherUid() {
3755         doTestFirewallCloseSocket(FIREWALL_CHAIN_STANDBY, FIREWALL_RULE_ALLOW,
3756                 Process.myUid() + 1, EXPECT_OPEN);
3757         doTestFirewallCloseSocket(FIREWALL_CHAIN_STANDBY, FIREWALL_RULE_DENY,
3758                 Process.myUid() + 1, EXPECT_OPEN);
3759     }
3760 
3761     private int getBlockedReason(final int chain) {
3762         switch(chain) {
3763             case FIREWALL_CHAIN_DOZABLE:
3764                 return BLOCKED_REASON_DOZE;
3765             case  FIREWALL_CHAIN_POWERSAVE:
3766                 return BLOCKED_REASON_BATTERY_SAVER;
3767             case  FIREWALL_CHAIN_RESTRICTED:
3768                 return BLOCKED_REASON_RESTRICTED_MODE;
3769             case  FIREWALL_CHAIN_LOW_POWER_STANDBY:
3770                 return BLOCKED_REASON_LOW_POWER_STANDBY;
3771             case  FIREWALL_CHAIN_BACKGROUND:
3772                 return BLOCKED_REASON_APP_BACKGROUND;
3773             case  FIREWALL_CHAIN_STANDBY:
3774                 return BLOCKED_REASON_APP_STANDBY;
3775             case FIREWALL_CHAIN_METERED_DENY_USER:
3776                 return BLOCKED_METERED_REASON_USER_RESTRICTED;
3777             case FIREWALL_CHAIN_METERED_DENY_ADMIN:
3778                 return BLOCKED_METERED_REASON_ADMIN_DISABLED;
3779             case FIREWALL_CHAIN_OEM_DENY_1:
3780             case FIREWALL_CHAIN_OEM_DENY_2:
3781             case FIREWALL_CHAIN_OEM_DENY_3:
3782                 return BLOCKED_REASON_OEM_DENY;
3783             default:
3784                 throw new IllegalArgumentException(
3785                         "Failed to find blockedReasons for chain: " + chain);
3786         }
3787     }
3788 
3789     private void doTestBlockedReasons_setUidFirewallRule(final int chain, final boolean metered)
3790             throws Exception {
3791         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
3792 
3793         // Store current Wi-Fi metered value and update metered value
3794         final Network currentWifiNetwork = mCtsNetUtils.ensureWifiConnected();
3795         final NetworkCapabilities wifiNetworkCapabilities = callWithShellPermissionIdentity(
3796                 () -> mCm.getNetworkCapabilities(currentWifiNetwork));
3797         final String ssid = unquoteSSID(wifiNetworkCapabilities.getSsid());
3798         final boolean oldMeteredValue = wifiNetworkCapabilities.isMetered();
3799         final Network wifiNetwork =
3800                 setWifiMeteredStatusAndWait(ssid, metered, true /* waitForValidation */);
3801 
3802         // Store current firewall chains status. This test operates on the chain that is passed in,
3803         // but also always operates on FIREWALL_CHAIN_METERED_DENY_USER to ensure that metered
3804         // chains are tested as well.
3805         final int myUid = Process.myUid();
3806         final boolean wasChainEnabled = runWithShellPermissionIdentity(
3807                 () -> mCm.getFirewallChainEnabled(chain), NETWORK_SETTINGS);
3808         final int previousFirewallRule = runWithShellPermissionIdentity(
3809                 () -> mCm.getUidFirewallRule(chain, myUid));
3810         final int previousMeteredDenyFirewallRule = runWithShellPermissionIdentity(
3811                 () -> mCm.getUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_USER, myUid));
3812 
3813         final DetailedBlockedStatusCallback cb = new DetailedBlockedStatusCallback();
3814         networkCallbackRule.requestNetwork(makeWifiNetworkRequest(), cb);
3815         testAndCleanup(() -> {
3816             int blockedReasonsWithoutChain = BLOCKED_REASON_NONE;
3817             int blockedReasonsWithChain = getBlockedReason(chain);
3818             int blockedReasonsWithChainAndLockDown =
3819                     getBlockedReason(chain) | BLOCKED_REASON_LOCKDOWN_VPN;
3820             if (metered) {
3821                 blockedReasonsWithoutChain |= BLOCKED_METERED_REASON_USER_RESTRICTED;
3822                 blockedReasonsWithChain |= BLOCKED_METERED_REASON_USER_RESTRICTED;
3823                 blockedReasonsWithChainAndLockDown |= BLOCKED_METERED_REASON_USER_RESTRICTED;
3824             }
3825 
3826             // Set RULE_DENY on target chain and metered deny chain
3827             runWithShellPermissionIdentity(() -> {
3828                 mCm.setFirewallChainEnabled(chain, true /* enable */);
3829                 mCm.setUidFirewallRule(chain, myUid, FIREWALL_RULE_DENY);
3830                 mCm.setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_USER, myUid,
3831                         FIREWALL_RULE_DENY);
3832             }, NETWORK_SETTINGS);
3833             cb.eventuallyExpectBlockedStatusCallback(wifiNetwork, blockedReasonsWithChain);
3834 
3835             // Set VPN lockdown
3836             final Range<Integer> myUidRange = new Range<>(myUid, myUid);
3837             runWithShellPermissionIdentity(() -> setRequireVpnForUids(
3838                     true /* requireVpn */, List.of(myUidRange)), NETWORK_SETTINGS);
3839             cb.eventuallyExpectBlockedStatusCallback(wifiNetwork,
3840                     blockedReasonsWithChainAndLockDown);
3841 
3842             // Unset VPN lockdown
3843             runWithShellPermissionIdentity(() -> setRequireVpnForUids(
3844                     false /* requireVpn */, List.of(myUidRange)), NETWORK_SETTINGS);
3845             cb.eventuallyExpectBlockedStatusCallback(wifiNetwork, blockedReasonsWithChain);
3846 
3847             // Set RULE_ALLOW on target chain
3848             runWithShellPermissionIdentity(
3849                     () -> mCm.setUidFirewallRule(chain, myUid, FIREWALL_RULE_ALLOW),
3850                     NETWORK_SETTINGS);
3851             cb.eventuallyExpectBlockedStatusCallback(wifiNetwork, blockedReasonsWithoutChain);
3852 
3853             // Set RULE_ALLOW on metered deny chain
3854             runWithShellPermissionIdentity(() -> mCm.setUidFirewallRule(
3855                             FIREWALL_CHAIN_METERED_DENY_USER, myUid, FIREWALL_RULE_ALLOW),
3856                     NETWORK_SETTINGS);
3857             if (metered) {
3858                 cb.eventuallyExpectBlockedStatusCallback(wifiNetwork, BLOCKED_REASON_NONE);
3859             }
3860         }, /* cleanup */ () -> {
3861             setWifiMeteredStatusAndWait(ssid, oldMeteredValue, false /* waitForValidation */);
3862         }, /* cleanup */ () -> {
3863             mCm.unregisterNetworkCallback(cb);
3864         }, /* cleanup */ () -> {
3865             runWithShellPermissionIdentity(() -> {
3866                 mCm.setFirewallChainEnabled(chain, wasChainEnabled);
3867                 try {
3868                     mCm.setUidFirewallRule(chain, myUid, previousFirewallRule);
3869                 } catch (IllegalStateException ignored) {
3870                     // Removing match causes an exception when the rule entry for the uid does
3871                     // not exist. But this is fine and can be ignored.
3872                 }
3873                 try {
3874                     mCm.setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_USER, myUid,
3875                             previousMeteredDenyFirewallRule);
3876                 } catch (IllegalStateException ignored) {
3877                     // Removing match causes an exception when the rule entry for the uid does
3878                     // not exist. But this is fine and can be ignored.
3879                 }
3880             }, NETWORK_SETTINGS);
3881         });
3882     }
3883 
3884     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
3885     @Test @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @ConnectivityModuleTest
3886     public void testBlockedReasons_setUidFirewallRule() throws Exception {
3887         doTestBlockedReasons_setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, true /* metered */);
3888         doTestBlockedReasons_setUidFirewallRule(FIREWALL_CHAIN_STANDBY, false /* metered */);
3889     }
3890 
3891     private void doTestBlockedReasons_setFirewallChainEnabled(final int chain) {
3892         // Store current firewall chains status.
3893         final int myUid = Process.myUid();
3894         // TODO(b/342508466): Use runAsShell
3895         final boolean wasChainEnabled = runWithShellPermissionIdentity(
3896                 () -> mCm.getFirewallChainEnabled(chain), NETWORK_SETTINGS);
3897         final int previousFirewallRule = runWithShellPermissionIdentity(
3898                 () -> mCm.getUidFirewallRule(chain, myUid), NETWORK_SETTINGS);
3899 
3900         final DetailedBlockedStatusCallback cb = new DetailedBlockedStatusCallback();
3901         networkCallbackRule.registerDefaultNetworkCallback(cb);
3902         final Network network = cb.expect(CallbackEntry.AVAILABLE).getNetwork();
3903         testAndCleanup(() -> {
3904             // Disable chain and set RULE_DENY on target chain
3905             runWithShellPermissionIdentity(() -> {
3906                 mCm.setFirewallChainEnabled(chain, false /* enable */);
3907                 mCm.setUidFirewallRule(chain, myUid, FIREWALL_RULE_DENY);
3908             }, NETWORK_SETTINGS);
3909             cb.eventuallyExpectBlockedStatusCallback(network, BLOCKED_REASON_NONE);
3910 
3911             // Enable chain
3912             runWithShellPermissionIdentity(() -> {
3913                 mCm.setFirewallChainEnabled(chain, true /* enable */);
3914             }, NETWORK_SETTINGS);
3915             cb.eventuallyExpectBlockedStatusCallback(network, getBlockedReason(chain));
3916 
3917             // Disable chain
3918             runWithShellPermissionIdentity(() -> {
3919                 mCm.setFirewallChainEnabled(chain, false /* enable */);
3920             }, NETWORK_SETTINGS);
3921             cb.eventuallyExpectBlockedStatusCallback(network, BLOCKED_REASON_NONE);
3922         }, /* cleanup */ () -> {
3923             runWithShellPermissionIdentity(() -> {
3924                 mCm.setFirewallChainEnabled(chain, wasChainEnabled);
3925                 try {
3926                     mCm.setUidFirewallRule(chain, myUid, previousFirewallRule);
3927                 } catch (IllegalStateException ignored) {
3928                     // Removing match causes an exception when the rule entry for the uid does
3929                     // not exist. But this is fine and can be ignored.
3930                 }
3931             }, NETWORK_SETTINGS);
3932         });
3933     }
3934 
3935     @Test @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @ConnectivityModuleTest
3936     public void testBlockedReasons_setFirewallChainEnabled() {
3937         doTestBlockedReasons_setFirewallChainEnabled(FIREWALL_CHAIN_POWERSAVE);
3938         doTestBlockedReasons_setFirewallChainEnabled(FIREWALL_CHAIN_OEM_DENY_1);
3939     }
3940 
3941     private void doTestBlockedReasons_replaceFirewallChain(
3942             final int chain, final boolean isAllowList) {
3943         // Store current firewall chains status.
3944         final int myUid = Process.myUid();
3945         final boolean wasChainEnabled = runWithShellPermissionIdentity(
3946                 () -> mCm.getFirewallChainEnabled(chain), NETWORK_SETTINGS);
3947         final int previousFirewallRule = runWithShellPermissionIdentity(
3948                 () -> mCm.getUidFirewallRule(chain, myUid), NETWORK_SETTINGS);
3949 
3950         final DetailedBlockedStatusCallback cb = new DetailedBlockedStatusCallback();
3951         networkCallbackRule.registerDefaultNetworkCallback(cb);
3952         final Network network = cb.expect(CallbackEntry.AVAILABLE).getNetwork();
3953         testAndCleanup(() -> {
3954             cb.eventuallyExpectBlockedStatusCallback(network, BLOCKED_REASON_NONE);
3955 
3956             // Remove uid from the target chain and enable chain
3957             runWithShellPermissionIdentity(() -> {
3958                 // Note that this removes *all* UIDs from the chain, not just the UID that is
3959                 // being tested. This is probably OK since FIREWALL_CHAIN_OEM_DENY_2 is unused
3960                 // in AOSP and FIREWALL_CHAIN_BACKGROUND is probably empty when running this
3961                 // test (since nothing is in the foreground).
3962                 //
3963                 // TODO(b/342508466): add a getFirewallUidChainContents or similar method to fetch
3964                 // chain contents, and update this test to use it.
3965                 mCm.replaceFirewallChain(chain, new int[0]);
3966                 mCm.setFirewallChainEnabled(chain, true /* enable */);
3967             }, NETWORK_SETTINGS);
3968 
3969             if (isAllowList) {
3970                 cb.eventuallyExpectBlockedStatusCallback(network, getBlockedReason(chain));
3971             } else {
3972                 cb.assertNoBlockedStatusCallback();
3973             }
3974 
3975             // Put uid on the target chain
3976             runWithShellPermissionIdentity(
3977                     () -> mCm.replaceFirewallChain(chain, new int[]{myUid}), NETWORK_SETTINGS);
3978 
3979             if (isAllowList) {
3980                 cb.eventuallyExpectBlockedStatusCallback(network, BLOCKED_REASON_NONE);
3981             } else {
3982                 cb.eventuallyExpectBlockedStatusCallback(network, getBlockedReason(chain));
3983             }
3984 
3985             // Remove uid from the target chain
3986             runWithShellPermissionIdentity(
3987                     () -> mCm.replaceFirewallChain(chain, new int[0]), NETWORK_SETTINGS);
3988 
3989             if (isAllowList) {
3990                 cb.eventuallyExpectBlockedStatusCallback(network, getBlockedReason(chain));
3991             } else {
3992                 cb.eventuallyExpectBlockedStatusCallback(network, BLOCKED_REASON_NONE);
3993             }
3994         }, /* cleanup */ () -> {
3995             runWithShellPermissionIdentity(() -> {
3996                 mCm.setFirewallChainEnabled(chain, wasChainEnabled);
3997                 try {
3998                     mCm.setUidFirewallRule(chain, myUid, previousFirewallRule);
3999                 } catch (IllegalStateException ignored) {
4000                     // Removing match causes an exception when the rule entry for the uid does
4001                     // not exist. But this is fine and can be ignored.
4002                 }
4003             }, NETWORK_SETTINGS);
4004         });
4005     }
4006 
4007     @Test @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @ConnectivityModuleTest
4008     public void testBlockedReasons_replaceFirewallChain() {
4009         doTestBlockedReasons_replaceFirewallChain(
4010                 FIREWALL_CHAIN_BACKGROUND, true /* isAllowChain */);
4011         doTestBlockedReasons_replaceFirewallChain(
4012                 FIREWALL_CHAIN_OEM_DENY_2, false /* isAllowChain */);
4013     }
4014 
4015     private void assumeTestSApis() {
4016         // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
4017         // shims, and @IgnoreUpTo does not check that.
4018         assumeTrue(TestUtils.shouldTestSApis());
4019     }
4020 }
4021