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.CONNECTIVITY_INTERNAL;
20 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
21 import static android.Manifest.permission.NETWORK_SETTINGS;
22 import static android.Manifest.permission.READ_DEVICE_CONFIG;
23 import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
24 import static android.content.pm.PackageManager.FEATURE_ETHERNET;
25 import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
26 import static android.content.pm.PackageManager.FEATURE_USB_HOST;
27 import static android.content.pm.PackageManager.FEATURE_WATCH;
28 import static android.content.pm.PackageManager.FEATURE_WIFI;
29 import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
30 import static android.content.pm.PackageManager.GET_PERMISSIONS;
31 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
32 import static android.net.ConnectivityManager.EXTRA_NETWORK;
33 import static android.net.ConnectivityManager.EXTRA_NETWORK_REQUEST;
34 import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
35 import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
36 import static android.net.ConnectivityManager.TYPE_ETHERNET;
37 import static android.net.ConnectivityManager.TYPE_MOBILE_CBS;
38 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
39 import static android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY;
40 import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
41 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
42 import static android.net.ConnectivityManager.TYPE_MOBILE_IA;
43 import static android.net.ConnectivityManager.TYPE_MOBILE_IMS;
44 import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
45 import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
46 import static android.net.ConnectivityManager.TYPE_PROXY;
47 import static android.net.ConnectivityManager.TYPE_VPN;
48 import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
49 import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
50 import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
51 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
52 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
53 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
54 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
55 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
56 import static android.net.NetworkCapabilities.TRANSPORT_TEST;
57 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
58 import static android.net.cts.util.CtsNetUtils.ConnectivityActionReceiver;
59 import static android.net.cts.util.CtsNetUtils.HTTP_PORT;
60 import static android.net.cts.util.CtsNetUtils.NETWORK_CALLBACK_ACTION;
61 import static android.net.cts.util.CtsNetUtils.TEST_HOST;
62 import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
63 import static android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback;
64 import static android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL;
65 import static android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL;
66 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
67 import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
68 import static android.system.OsConstants.AF_INET;
69 import static android.system.OsConstants.AF_INET6;
70 import static android.system.OsConstants.AF_UNSPEC;
71 
72 import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
73 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
74 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
75 import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
76 import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_LOCKDOWN_VPN;
77 import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_NONE;
78 import static com.android.testutils.MiscAsserts.assertThrows;
79 import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
80 import static com.android.testutils.TestPermissionUtil.runAsShell;
81 
82 import static org.junit.Assert.assertArrayEquals;
83 import static org.junit.Assert.assertEquals;
84 import static org.junit.Assert.assertFalse;
85 import static org.junit.Assert.assertNotEquals;
86 import static org.junit.Assert.assertNotNull;
87 import static org.junit.Assert.assertNotSame;
88 import static org.junit.Assert.assertNull;
89 import static org.junit.Assert.assertTrue;
90 import static org.junit.Assert.fail;
91 import static org.junit.Assume.assumeTrue;
92 
93 import android.annotation.NonNull;
94 import android.app.Instrumentation;
95 import android.app.PendingIntent;
96 import android.app.UiAutomation;
97 import android.content.BroadcastReceiver;
98 import android.content.ContentResolver;
99 import android.content.Context;
100 import android.content.Intent;
101 import android.content.IntentFilter;
102 import android.content.pm.PackageInfo;
103 import android.content.pm.PackageManager;
104 import android.content.res.Resources;
105 import android.net.ConnectivityManager;
106 import android.net.ConnectivityManager.NetworkCallback;
107 import android.net.ConnectivitySettingsManager;
108 import android.net.InetAddresses;
109 import android.net.IpSecManager;
110 import android.net.IpSecManager.UdpEncapsulationSocket;
111 import android.net.LinkAddress;
112 import android.net.LinkProperties;
113 import android.net.Network;
114 import android.net.NetworkAgent;
115 import android.net.NetworkAgentConfig;
116 import android.net.NetworkCapabilities;
117 import android.net.NetworkInfo;
118 import android.net.NetworkInfo.DetailedState;
119 import android.net.NetworkInfo.State;
120 import android.net.NetworkProvider;
121 import android.net.NetworkRequest;
122 import android.net.NetworkScore;
123 import android.net.NetworkSpecifier;
124 import android.net.NetworkStateSnapshot;
125 import android.net.NetworkUtils;
126 import android.net.OemNetworkPreferences;
127 import android.net.ProxyInfo;
128 import android.net.SocketKeepalive;
129 import android.net.TelephonyNetworkSpecifier;
130 import android.net.TestNetworkInterface;
131 import android.net.TestNetworkManager;
132 import android.net.Uri;
133 import android.net.cts.util.CtsNetUtils;
134 import android.net.cts.util.CtsTetheringUtils;
135 import android.net.util.KeepaliveUtils;
136 import android.net.wifi.WifiManager;
137 import android.os.Binder;
138 import android.os.Build;
139 import android.os.Handler;
140 import android.os.Looper;
141 import android.os.MessageQueue;
142 import android.os.Process;
143 import android.os.SystemClock;
144 import android.os.SystemProperties;
145 import android.os.UserHandle;
146 import android.os.VintfRuntimeInfo;
147 import android.platform.test.annotations.AppModeFull;
148 import android.provider.DeviceConfig;
149 import android.provider.Settings;
150 import android.telephony.SubscriptionManager;
151 import android.telephony.TelephonyManager;
152 import android.text.TextUtils;
153 import android.util.ArraySet;
154 import android.util.Log;
155 import android.util.Pair;
156 import android.util.Range;
157 
158 import androidx.test.InstrumentationRegistry;
159 import androidx.test.runner.AndroidJUnit4;
160 
161 import com.android.internal.util.ArrayUtils;
162 import com.android.modules.utils.build.SdkLevel;
163 import com.android.networkstack.apishim.ConnectivityManagerShimImpl;
164 import com.android.networkstack.apishim.ConstantsShim;
165 import com.android.networkstack.apishim.NetworkInformationShimImpl;
166 import com.android.networkstack.apishim.common.ConnectivityManagerShim;
167 import com.android.testutils.CompatUtil;
168 import com.android.testutils.DevSdkIgnoreRule;
169 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
170 import com.android.testutils.DevSdkIgnoreRuleKt;
171 import com.android.testutils.RecorderCallback.CallbackEntry;
172 import com.android.testutils.SkipPresubmit;
173 import com.android.testutils.TestHttpServer;
174 import com.android.testutils.TestNetworkTracker;
175 import com.android.testutils.TestableNetworkCallback;
176 
177 import junit.framework.AssertionFailedError;
178 
179 import libcore.io.Streams;
180 
181 import org.junit.After;
182 import org.junit.Before;
183 import org.junit.Rule;
184 import org.junit.Test;
185 import org.junit.runner.RunWith;
186 
187 import java.io.FileDescriptor;
188 import java.io.IOException;
189 import java.io.InputStream;
190 import java.io.InputStreamReader;
191 import java.io.OutputStream;
192 import java.net.HttpURLConnection;
193 import java.net.Inet4Address;
194 import java.net.Inet6Address;
195 import java.net.InetAddress;
196 import java.net.InetSocketAddress;
197 import java.net.MalformedURLException;
198 import java.net.Socket;
199 import java.net.URL;
200 import java.net.UnknownHostException;
201 import java.nio.charset.StandardCharsets;
202 import java.util.ArrayList;
203 import java.util.Collection;
204 import java.util.List;
205 import java.util.Objects;
206 import java.util.Set;
207 import java.util.concurrent.CompletableFuture;
208 import java.util.concurrent.CountDownLatch;
209 import java.util.concurrent.Executor;
210 import java.util.concurrent.ExecutorService;
211 import java.util.concurrent.Executors;
212 import java.util.concurrent.LinkedBlockingQueue;
213 import java.util.concurrent.TimeUnit;
214 import java.util.concurrent.TimeoutException;
215 import java.util.concurrent.atomic.AtomicInteger;
216 import java.util.function.Supplier;
217 import java.util.regex.Matcher;
218 import java.util.regex.Pattern;
219 
220 import fi.iki.elonen.NanoHTTPD.Method;
221 import fi.iki.elonen.NanoHTTPD.Response.IStatus;
222 import fi.iki.elonen.NanoHTTPD.Response.Status;
223 
224 @RunWith(AndroidJUnit4.class)
225 public class ConnectivityManagerTest {
226     @Rule
227     public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
228 
229     private static final String TAG = ConnectivityManagerTest.class.getSimpleName();
230 
231     public static final int TYPE_MOBILE = ConnectivityManager.TYPE_MOBILE;
232     public static final int TYPE_WIFI = ConnectivityManager.TYPE_WIFI;
233 
234     private static final int HOST_ADDRESS = 0x7f000001;// represent ip 127.0.0.1
235     private static final int KEEPALIVE_CALLBACK_TIMEOUT_MS = 2000;
236     private static final int INTERVAL_KEEPALIVE_RETRY_MS = 500;
237     private static final int MAX_KEEPALIVE_RETRY_COUNT = 3;
238     private static final int MIN_KEEPALIVE_INTERVAL = 10;
239 
240     private static final int NETWORK_CALLBACK_TIMEOUT_MS = 30_000;
241     private static final int NO_CALLBACK_TIMEOUT_MS = 100;
242     private static final int NUM_TRIES_MULTIPATH_PREF_CHECK = 20;
243     private static final long INTERVAL_MULTIPATH_PREF_CHECK_MS = 500;
244     // device could have only one interface: data, wifi.
245     private static final int MIN_NUM_NETWORK_TYPES = 1;
246 
247     // Airplane Mode BroadcastReceiver Timeout
248     private static final long AIRPLANE_MODE_CHANGE_TIMEOUT_MS = 10_000L;
249 
250     // Timeout for applying uids allowed on restricted networks
251     private static final long APPLYING_UIDS_ALLOWED_ON_RESTRICTED_NETWORKS_TIMEOUT_MS = 3_000L;
252 
253     // Minimum supported keepalive counts for wifi and cellular.
254     public static final int MIN_SUPPORTED_CELLULAR_KEEPALIVE_COUNT = 1;
255     public static final int MIN_SUPPORTED_WIFI_KEEPALIVE_COUNT = 3;
256 
257     private static final String NETWORK_METERED_MULTIPATH_PREFERENCE_RES_NAME =
258             "config_networkMeteredMultipathPreference";
259     private static final String KEEPALIVE_ALLOWED_UNPRIVILEGED_RES_NAME =
260             "config_allowedUnprivilegedKeepalivePerUid";
261     private static final String KEEPALIVE_RESERVED_PER_SLOT_RES_NAME =
262             "config_reservedPrivilegedKeepaliveSlots";
263 
264     private static final LinkAddress TEST_LINKADDR = new LinkAddress(
265             InetAddresses.parseNumericAddress("2001:db8::8"), 64);
266 
267     private static final int AIRPLANE_MODE_OFF = 0;
268     private static final int AIRPLANE_MODE_ON = 1;
269 
270     private static final String TEST_HTTPS_URL_PATH = "/https_path";
271     private static final String TEST_HTTP_URL_PATH = "/http_path";
272     private static final String LOCALHOST_HOSTNAME = "localhost";
273     // Re-connecting to the AP, obtaining an IP address, revalidating can take a long time
274     private static final long WIFI_CONNECT_TIMEOUT_MS = 60_000L;
275 
276     private Context mContext;
277     private Instrumentation mInstrumentation;
278     private ConnectivityManager mCm;
279     private ConnectivityManagerShim mCmShim;
280     private WifiManager mWifiManager;
281     private PackageManager mPackageManager;
282     private final ArraySet<Integer> mNetworkTypes = new ArraySet<>();
283     private UiAutomation mUiAutomation;
284     private CtsNetUtils mCtsNetUtils;
285 
286     // Used for cleanup purposes.
287     private final List<Range<Integer>> mVpnRequiredUidRanges = new ArrayList<>();
288 
289     private final TestHttpServer mHttpServer = new TestHttpServer(LOCALHOST_HOSTNAME);
290 
291     @Before
setUp()292     public void setUp() throws Exception {
293         mInstrumentation = InstrumentationRegistry.getInstrumentation();
294         mContext = mInstrumentation.getContext();
295         mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
296         mCmShim = ConnectivityManagerShimImpl.newInstance(mContext);
297         mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
298         mPackageManager = mContext.getPackageManager();
299         mCtsNetUtils = new CtsNetUtils(mContext);
300 
301         if (DevSdkIgnoreRuleKt.isDevSdkInRange(null /* minExclusive */,
302                 Build.VERSION_CODES.R /* maxInclusive */)) {
303             addLegacySupportedNetworkTypes();
304         } else {
305             addSupportedNetworkTypes();
306         }
307 
308         mUiAutomation = mInstrumentation.getUiAutomation();
309 
310         assertNotNull("CTS requires a working Internet connection", mCm.getActiveNetwork());
311     }
312 
addLegacySupportedNetworkTypes()313     private void addLegacySupportedNetworkTypes() {
314         // Network type support as expected for android R-
315         // Get com.android.internal.R.array.networkAttributes
316         int resId = mContext.getResources().getIdentifier("networkAttributes", "array", "android");
317         String[] naStrings = mContext.getResources().getStringArray(resId);
318         boolean wifiOnly = SystemProperties.getBoolean("ro.radio.noril", false);
319         for (String naString : naStrings) {
320             try {
321                 final String[] splitConfig = naString.split(",");
322                 // Format was name,type,radio,priority,restoreTime,dependencyMet
323                 final int type = Integer.parseInt(splitConfig[1]);
324                 if (wifiOnly && ConnectivityManager.isNetworkTypeMobile(type)) {
325                     continue;
326                 }
327                 mNetworkTypes.add(type);
328             } catch (Exception e) {}
329         }
330     }
331 
addSupportedNetworkTypes()332     private void addSupportedNetworkTypes() {
333         final PackageManager pm = mContext.getPackageManager();
334         if (pm.hasSystemFeature(FEATURE_WIFI)) {
335             mNetworkTypes.add(TYPE_WIFI);
336         }
337         if (pm.hasSystemFeature(FEATURE_WIFI_DIRECT)) {
338             mNetworkTypes.add(TYPE_WIFI_P2P);
339         }
340         if (mContext.getSystemService(TelephonyManager.class).isDataCapable()) {
341             mNetworkTypes.add(TYPE_MOBILE);
342             mNetworkTypes.add(TYPE_MOBILE_MMS);
343             mNetworkTypes.add(TYPE_MOBILE_SUPL);
344             mNetworkTypes.add(TYPE_MOBILE_DUN);
345             mNetworkTypes.add(TYPE_MOBILE_HIPRI);
346             mNetworkTypes.add(TYPE_MOBILE_FOTA);
347             mNetworkTypes.add(TYPE_MOBILE_IMS);
348             mNetworkTypes.add(TYPE_MOBILE_CBS);
349             mNetworkTypes.add(TYPE_MOBILE_IA);
350             mNetworkTypes.add(TYPE_MOBILE_EMERGENCY);
351         }
352         if (pm.hasSystemFeature(FEATURE_BLUETOOTH)) {
353             mNetworkTypes.add(TYPE_BLUETOOTH);
354         }
355         if (pm.hasSystemFeature(FEATURE_WATCH)) {
356             mNetworkTypes.add(TYPE_PROXY);
357         }
358         if (mContext.getSystemService(Context.ETHERNET_SERVICE) != null) {
359             mNetworkTypes.add(TYPE_ETHERNET);
360         }
361         mNetworkTypes.add(TYPE_VPN);
362     }
363 
364     @After
tearDown()365     public void tearDown() throws Exception {
366         // Release any NetworkRequests filed to connect mobile data.
367         if (mCtsNetUtils.cellConnectAttempted()) {
368             mCtsNetUtils.disconnectFromCell();
369         }
370 
371         if (TestUtils.shouldTestSApis()) {
372             runWithShellPermissionIdentity(
373                     () -> mCmShim.setRequireVpnForUids(false, mVpnRequiredUidRanges),
374                     NETWORK_SETTINGS);
375         }
376 
377         // All tests in this class require a working Internet connection as they start. Make
378         // sure there is still one as they end that's ready to use for the next test to use.
379         final TestNetworkCallback callback = new TestNetworkCallback();
380         mCm.registerDefaultNetworkCallback(callback);
381         try {
382             assertNotNull("Couldn't restore Internet connectivity", callback.waitForAvailable());
383         } finally {
384             mCm.unregisterNetworkCallback(callback);
385         }
386     }
387 
388     @Test
testIsNetworkTypeValid()389     public void testIsNetworkTypeValid() {
390         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE));
391         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_WIFI));
392         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_MMS));
393         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_SUPL));
394         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_DUN));
395         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_HIPRI));
396         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_WIMAX));
397         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_BLUETOOTH));
398         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_DUMMY));
399         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_ETHERNET));
400         assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_FOTA));
401         assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_IMS));
402         assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_CBS));
403         assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_WIFI_P2P));
404         assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_IA));
405         assertFalse(mCm.isNetworkTypeValid(-1));
406         assertTrue(mCm.isNetworkTypeValid(0));
407         assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.MAX_NETWORK_TYPE));
408         assertFalse(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.MAX_NETWORK_TYPE+1));
409 
410         NetworkInfo[] ni = mCm.getAllNetworkInfo();
411 
412         for (NetworkInfo n: ni) {
413             assertTrue(ConnectivityManager.isNetworkTypeValid(n.getType()));
414         }
415 
416     }
417 
418     @Test
testSetNetworkPreference()419     public void testSetNetworkPreference() {
420         // getNetworkPreference() and setNetworkPreference() are both deprecated so they do
421         // not preform any action.  Verify they are at least still callable.
422         mCm.setNetworkPreference(mCm.getNetworkPreference());
423     }
424 
425     @Test
testGetActiveNetworkInfo()426     public void testGetActiveNetworkInfo() {
427         NetworkInfo ni = mCm.getActiveNetworkInfo();
428 
429         assertNotNull("You must have an active network connection to complete CTS", ni);
430         assertTrue(ConnectivityManager.isNetworkTypeValid(ni.getType()));
431         assertTrue(ni.getState() == State.CONNECTED);
432     }
433 
434     @Test
testGetActiveNetwork()435     public void testGetActiveNetwork() {
436         Network network = mCm.getActiveNetwork();
437         assertNotNull("You must have an active network connection to complete CTS", network);
438 
439         NetworkInfo ni = mCm.getNetworkInfo(network);
440         assertNotNull("Network returned from getActiveNetwork was invalid", ni);
441 
442         // Similar to testGetActiveNetworkInfo above.
443         assertTrue(ConnectivityManager.isNetworkTypeValid(ni.getType()));
444         assertTrue(ni.getState() == State.CONNECTED);
445     }
446 
447     @Test
testGetNetworkInfo()448     public void testGetNetworkInfo() {
449         for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE+1; type++) {
450             if (shouldBeSupported(type)) {
451                 NetworkInfo ni = mCm.getNetworkInfo(type);
452                 assertTrue("Info shouldn't be null for " + type, ni != null);
453                 State state = ni.getState();
454                 assertTrue("Bad state for " + type, State.UNKNOWN.ordinal() >= state.ordinal()
455                            && state.ordinal() >= State.CONNECTING.ordinal());
456                 DetailedState ds = ni.getDetailedState();
457                 assertTrue("Bad detailed state for " + type,
458                            DetailedState.FAILED.ordinal() >= ds.ordinal()
459                            && ds.ordinal() >= DetailedState.IDLE.ordinal());
460             } else {
461                 assertNull("Info should be null for " + type, mCm.getNetworkInfo(type));
462             }
463         }
464     }
465 
466     @Test
testGetAllNetworkInfo()467     public void testGetAllNetworkInfo() {
468         NetworkInfo[] ni = mCm.getAllNetworkInfo();
469         assertTrue(ni.length >= MIN_NUM_NETWORK_TYPES);
470         for (int type = 0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
471             int desiredFoundCount = (shouldBeSupported(type) ? 1 : 0);
472             int foundCount = 0;
473             for (NetworkInfo i : ni) {
474                 if (i.getType() == type) foundCount++;
475             }
476             if (foundCount != desiredFoundCount) {
477                 Log.e(TAG, "failure in testGetAllNetworkInfo.  Dump of returned NetworkInfos:");
478                 for (NetworkInfo networkInfo : ni) Log.e(TAG, "  " + networkInfo);
479             }
480             assertTrue("Unexpected foundCount of " + foundCount + " for type " + type,
481                     foundCount == desiredFoundCount);
482         }
483     }
484 
getSubscriberIdForCellNetwork(Network cellNetwork)485     private String getSubscriberIdForCellNetwork(Network cellNetwork) {
486         final NetworkCapabilities cellCaps = mCm.getNetworkCapabilities(cellNetwork);
487         final NetworkSpecifier specifier = cellCaps.getNetworkSpecifier();
488         assertTrue(specifier instanceof TelephonyNetworkSpecifier);
489         // Get subscription from Telephony network specifier.
490         final int subId = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
491         assertNotEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID, subId);
492 
493         // Get subscriber Id from telephony manager.
494         final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
495         return runWithShellPermissionIdentity(() -> tm.getSubscriberId(subId),
496                 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
497     }
498 
499     @AppModeFull(reason = "Cannot request network in instant app mode")
500     @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
501     @Test
testGetAllNetworkStateSnapshots()502     public void testGetAllNetworkStateSnapshots()
503             throws InterruptedException {
504         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
505         // Make sure cell is active to retrieve IMSI for verification in later step.
506         final Network cellNetwork = mCtsNetUtils.connectToCell();
507         final String subscriberId = getSubscriberIdForCellNetwork(cellNetwork);
508         assertFalse(TextUtils.isEmpty(subscriberId));
509 
510         // Verify the API cannot be called without proper permission.
511         assertThrows(SecurityException.class, () -> mCm.getAllNetworkStateSnapshots());
512 
513         // Get all networks, verify the result of getAllNetworkStateSnapshots matches the result
514         // got from other APIs.
515         final Network[] networks = mCm.getAllNetworks();
516         assertGreaterOrEqual(networks.length, 1);
517         final List<NetworkStateSnapshot> snapshots = runWithShellPermissionIdentity(
518                 () -> mCm.getAllNetworkStateSnapshots(), NETWORK_SETTINGS);
519         assertEquals(networks.length, snapshots.size());
520         for (final Network network : networks) {
521             // Can't use a lambda because it will cause the test to crash on R with
522             // NoClassDefFoundError.
523             NetworkStateSnapshot snapshot = null;
524             for (NetworkStateSnapshot item : snapshots) {
525                 if (item.getNetwork().equals(network)) {
526                     snapshot = item;
527                     break;
528                 }
529             }
530             assertNotNull(snapshot);
531             final NetworkCapabilities caps =
532                     Objects.requireNonNull(mCm.getNetworkCapabilities(network));
533             // Redact specifier of the capabilities of the snapshot before comparing since
534             // the result returned from getNetworkCapabilities always get redacted.
535             final NetworkSpecifier snapshotCapSpecifier =
536                     snapshot.getNetworkCapabilities().getNetworkSpecifier();
537             final NetworkSpecifier redactedSnapshotCapSpecifier =
538                     snapshotCapSpecifier == null ? null : snapshotCapSpecifier.redact();
539             assertEquals("", caps.describeImmutableDifferences(
540                     snapshot.getNetworkCapabilities()
541                             .setNetworkSpecifier(redactedSnapshotCapSpecifier)));
542             assertEquals(mCm.getLinkProperties(network), snapshot.getLinkProperties());
543             assertEquals(mCm.getNetworkInfo(network).getType(), snapshot.getLegacyType());
544 
545             if (network.equals(cellNetwork)) {
546                 assertEquals(subscriberId, snapshot.getSubscriberId());
547             }
548         }
549     }
550 
551     /**
552      * Tests that connections can be opened on WiFi and cellphone networks,
553      * and that they are made from different IP addresses.
554      */
555     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
556     @Test
557     @SkipPresubmit(reason = "Virtual devices use a single internet connection for all networks")
testOpenConnection()558     public void testOpenConnection() throws Exception {
559         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
560         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
561 
562         Network wifiNetwork = mCtsNetUtils.connectToWifi();
563         Network cellNetwork = mCtsNetUtils.connectToCell();
564         // This server returns the requestor's IP address as the response body.
565         URL url = new URL("http://google-ipv6test.appspot.com/ip.js?fmt=text");
566         String wifiAddressString = httpGet(wifiNetwork, url);
567         String cellAddressString = httpGet(cellNetwork, url);
568 
569         assertFalse(String.format("Same address '%s' on two different networks (%s, %s)",
570                 wifiAddressString, wifiNetwork, cellNetwork),
571                 wifiAddressString.equals(cellAddressString));
572 
573         // Verify that the IP addresses that the requests appeared to come from are actually on the
574         // respective networks.
575         assertOnNetwork(wifiAddressString, wifiNetwork);
576         assertOnNetwork(cellAddressString, cellNetwork);
577 
578         assertFalse("Unexpectedly equal: " + wifiNetwork, wifiNetwork.equals(cellNetwork));
579     }
580 
581     /**
582      * Performs a HTTP GET to the specified URL on the specified Network, and returns
583      * the response body decoded as UTF-8.
584      */
httpGet(Network network, URL httpUrl)585     private static String httpGet(Network network, URL httpUrl) throws IOException {
586         HttpURLConnection connection = (HttpURLConnection) network.openConnection(httpUrl);
587         try {
588             InputStream inputStream = connection.getInputStream();
589             return Streams.readFully(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
590         } finally {
591             connection.disconnect();
592         }
593     }
594 
assertOnNetwork(String adressString, Network network)595     private void assertOnNetwork(String adressString, Network network) throws UnknownHostException {
596         InetAddress address = InetAddress.getByName(adressString);
597         LinkProperties linkProperties = mCm.getLinkProperties(network);
598         // To make sure that the request went out on the right network, check that
599         // the IP address seen by the server is assigned to the expected network.
600         // We can only do this for IPv6 addresses, because in IPv4 we will likely
601         // have a private IPv4 address, and that won't match what the server sees.
602         if (address instanceof Inet6Address) {
603             assertContains(linkProperties.getAddresses(), address);
604         }
605     }
606 
assertContains(Collection<T> collection, T element)607     private static<T> void assertContains(Collection<T> collection, T element) {
608         assertTrue(element + " not found in " + collection, collection.contains(element));
609     }
610 
assertStartUsingNetworkFeatureUnsupported(int networkType, String feature)611     private void assertStartUsingNetworkFeatureUnsupported(int networkType, String feature) {
612         try {
613             mCm.startUsingNetworkFeature(networkType, feature);
614             fail("startUsingNetworkFeature is no longer supported in the current API version");
615         } catch (UnsupportedOperationException expected) {}
616     }
617 
assertStopUsingNetworkFeatureUnsupported(int networkType, String feature)618     private void assertStopUsingNetworkFeatureUnsupported(int networkType, String feature) {
619         try {
620             mCm.startUsingNetworkFeature(networkType, feature);
621             fail("stopUsingNetworkFeature is no longer supported in the current API version");
622         } catch (UnsupportedOperationException expected) {}
623     }
624 
assertRequestRouteToHostUnsupported(int networkType, int hostAddress)625     private void assertRequestRouteToHostUnsupported(int networkType, int hostAddress) {
626         try {
627             mCm.requestRouteToHost(networkType, hostAddress);
628             fail("requestRouteToHost is no longer supported in the current API version");
629         } catch (UnsupportedOperationException expected) {}
630     }
631 
632     @Test
testStartUsingNetworkFeature()633     public void testStartUsingNetworkFeature() {
634 
635         final String invalidateFeature = "invalidateFeature";
636         final String mmsFeature = "enableMMS";
637 
638         assertStartUsingNetworkFeatureUnsupported(TYPE_MOBILE, invalidateFeature);
639         assertStopUsingNetworkFeatureUnsupported(TYPE_MOBILE, invalidateFeature);
640         assertStartUsingNetworkFeatureUnsupported(TYPE_WIFI, mmsFeature);
641     }
642 
shouldEthernetBeSupported()643     private boolean shouldEthernetBeSupported() {
644         // Instant mode apps aren't allowed to query the Ethernet service due to selinux policies.
645         // When in instant mode, don't fail if the Ethernet service is available. Instead, rely on
646         // the fact that Ethernet should be supported if the device has a hardware Ethernet port, or
647         // if the device can be a USB host and thus can use USB Ethernet adapters.
648         //
649         // Note that this test this will still fail in instant mode if a device supports Ethernet
650         // via other hardware means. We are not currently aware of any such device.
651         return (mContext.getSystemService(Context.ETHERNET_SERVICE) != null) ||
652             mPackageManager.hasSystemFeature(FEATURE_ETHERNET) ||
653             mPackageManager.hasSystemFeature(FEATURE_USB_HOST);
654     }
655 
shouldBeSupported(int networkType)656     private boolean shouldBeSupported(int networkType) {
657         return mNetworkTypes.contains(networkType)
658             || (networkType == ConnectivityManager.TYPE_VPN)
659             || (networkType == ConnectivityManager.TYPE_ETHERNET && shouldEthernetBeSupported());
660     }
661 
662     @Test
testIsNetworkSupported()663     public void testIsNetworkSupported() {
664         for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
665             boolean supported = mCm.isNetworkSupported(type);
666             if (shouldBeSupported(type)) {
667                 assertTrue("Network type " + type + " should be supported", supported);
668             } else {
669                 assertFalse("Network type " + type + " should not be supported", supported);
670             }
671         }
672     }
673 
674     @Test
testRequestRouteToHost()675     public void testRequestRouteToHost() {
676         for (int type = -1 ; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
677             assertRequestRouteToHostUnsupported(type, HOST_ADDRESS);
678         }
679     }
680 
681     @Test
testTest()682     public void testTest() {
683         mCm.getBackgroundDataSetting();
684     }
685 
makeDefaultRequest()686     private NetworkRequest makeDefaultRequest() {
687         // Make a request that is similar to the way framework tracks the system
688         // default network.
689         return new NetworkRequest.Builder()
690                 .clearCapabilities()
691                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
692                 .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
693                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
694                 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
695                 .build();
696     }
697 
makeWifiNetworkRequest()698     private NetworkRequest makeWifiNetworkRequest() {
699         return new NetworkRequest.Builder()
700                 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
701                 .addCapability(NET_CAPABILITY_INTERNET)
702                 .build();
703     }
704 
makeCellNetworkRequest()705     private NetworkRequest makeCellNetworkRequest() {
706         return new NetworkRequest.Builder()
707                 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
708                 .addCapability(NET_CAPABILITY_INTERNET)
709                 .build();
710     }
711 
712     @AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
713     @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
testIsPrivateDnsBroken()714     public void testIsPrivateDnsBroken() throws InterruptedException {
715         final String invalidPrivateDnsServer = "invalidhostname.example.com";
716         final String goodPrivateDnsServer = "dns.google";
717         mCtsNetUtils.storePrivateDnsSetting();
718         final TestableNetworkCallback cb = new TestableNetworkCallback();
719         mCm.registerNetworkCallback(makeWifiNetworkRequest(), cb);
720         try {
721             // Verifying the good private DNS sever
722             mCtsNetUtils.setPrivateDnsStrictMode(goodPrivateDnsServer);
723             final Network networkForPrivateDns =  mCtsNetUtils.ensureWifiConnected();
724             cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, NETWORK_CALLBACK_TIMEOUT_MS,
725                     entry -> (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
726                     .isPrivateDnsBroken()) && networkForPrivateDns.equals(entry.getNetwork()));
727 
728             // Verifying the broken private DNS sever
729             mCtsNetUtils.setPrivateDnsStrictMode(invalidPrivateDnsServer);
730             cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, NETWORK_CALLBACK_TIMEOUT_MS,
731                     entry -> (((CallbackEntry.CapabilitiesChanged) entry).getCaps()
732                     .isPrivateDnsBroken()) && networkForPrivateDns.equals(entry.getNetwork()));
733         } finally {
734             mCtsNetUtils.restorePrivateDnsSetting();
735             // Toggle wifi to make sure it is re-validated
736             reconnectWifi();
737         }
738     }
739 
740     /**
741      * Exercises both registerNetworkCallback and unregisterNetworkCallback. This checks to
742      * see if we get a callback for the TRANSPORT_WIFI transport type being available.
743      *
744      * <p>In order to test that a NetworkCallback occurs, we need some change in the network
745      * state (either a transport or capability is now available). The most straightforward is
746      * WiFi. We could add a version that uses the telephony data connection but it's not clear
747      * that it would increase test coverage by much (how many devices have 3G radio but not Wifi?).
748      */
749     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
750     @Test
testRegisterNetworkCallback()751     public void testRegisterNetworkCallback() {
752         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
753 
754         // We will register for a WIFI network being available or lost.
755         final TestNetworkCallback callback = new TestNetworkCallback();
756         mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
757 
758         final TestNetworkCallback defaultTrackingCallback = new TestNetworkCallback();
759         mCm.registerDefaultNetworkCallback(defaultTrackingCallback);
760 
761         final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback();
762         final TestNetworkCallback perUidCallback = new TestNetworkCallback();
763         final TestNetworkCallback bestMatchingCallback = new TestNetworkCallback();
764         final Handler h = new Handler(Looper.getMainLooper());
765         if (TestUtils.shouldTestSApis()) {
766             runWithShellPermissionIdentity(() -> {
767                 mCmShim.registerSystemDefaultNetworkCallback(systemDefaultCallback, h);
768                 mCmShim.registerDefaultNetworkCallbackForUid(Process.myUid(), perUidCallback, h);
769             }, NETWORK_SETTINGS);
770             mCm.registerBestMatchingNetworkCallback(makeDefaultRequest(), bestMatchingCallback, h);
771         }
772 
773         Network wifiNetwork = null;
774 
775         try {
776             mCtsNetUtils.ensureWifiConnected();
777 
778             // Now we should expect to get a network callback about availability of the wifi
779             // network even if it was already connected as a state-based action when the callback
780             // is registered.
781             wifiNetwork = callback.waitForAvailable();
782             assertNotNull("Did not receive onAvailable for TRANSPORT_WIFI request",
783                     wifiNetwork);
784 
785             final Network defaultNetwork = defaultTrackingCallback.waitForAvailable();
786             assertNotNull("Did not receive onAvailable on default network callback",
787                     defaultNetwork);
788 
789             if (TestUtils.shouldTestSApis()) {
790                 assertNotNull("Did not receive onAvailable on system default network callback",
791                         systemDefaultCallback.waitForAvailable());
792                 final Network perUidNetwork = perUidCallback.waitForAvailable();
793                 assertNotNull("Did not receive onAvailable on per-UID default network callback",
794                         perUidNetwork);
795                 assertEquals(defaultNetwork, perUidNetwork);
796                 final Network bestMatchingNetwork = bestMatchingCallback.waitForAvailable();
797                 assertNotNull("Did not receive onAvailable on best matching network callback",
798                         bestMatchingNetwork);
799                 assertEquals(defaultNetwork, bestMatchingNetwork);
800             }
801 
802         } catch (InterruptedException e) {
803             fail("Broadcast receiver or NetworkCallback wait was interrupted.");
804         } finally {
805             mCm.unregisterNetworkCallback(callback);
806             mCm.unregisterNetworkCallback(defaultTrackingCallback);
807             if (TestUtils.shouldTestSApis()) {
808                 mCm.unregisterNetworkCallback(systemDefaultCallback);
809                 mCm.unregisterNetworkCallback(perUidCallback);
810                 mCm.unregisterNetworkCallback(bestMatchingCallback);
811             }
812         }
813     }
814 
815     /**
816      * Tests both registerNetworkCallback and unregisterNetworkCallback similarly to
817      * {@link #testRegisterNetworkCallback} except that a {@code PendingIntent} is used instead
818      * of a {@code NetworkCallback}.
819      */
820     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
821     @Test
testRegisterNetworkCallback_withPendingIntent()822     public void testRegisterNetworkCallback_withPendingIntent() {
823         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
824 
825         // Create a ConnectivityActionReceiver that has an IntentFilter for our locally defined
826         // action, NETWORK_CALLBACK_ACTION.
827         final IntentFilter filter = new IntentFilter();
828         filter.addAction(NETWORK_CALLBACK_ACTION);
829 
830         final ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
831                 mCm, ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED);
832         mContext.registerReceiver(receiver, filter);
833 
834         // Create a broadcast PendingIntent for NETWORK_CALLBACK_ACTION.
835         final Intent intent = new Intent(NETWORK_CALLBACK_ACTION)
836                 .setPackage(mContext.getPackageName());
837         // While ConnectivityService would put extra info such as network or request id before
838         // broadcasting the inner intent. The MUTABLE flag needs to be added accordingly.
839         // TODO: replace with PendingIntent.FLAG_MUTABLE when this code compiles against S+ or
840         //  shims.
841         final int pendingIntentFlagMutable = 1 << 25;
842         final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0 /*requestCode*/,
843                 intent, PendingIntent.FLAG_CANCEL_CURRENT | pendingIntentFlagMutable);
844 
845         // We will register for a WIFI network being available or lost.
846         mCm.registerNetworkCallback(makeWifiNetworkRequest(), pendingIntent);
847 
848         try {
849             mCtsNetUtils.ensureWifiConnected();
850 
851             // Now we expect to get the Intent delivered notifying of the availability of the wifi
852             // network even if it was already connected as a state-based action when the callback
853             // is registered.
854             assertTrue("Did not receive expected Intent " + intent + " for TRANSPORT_WIFI",
855                     receiver.waitForState());
856         } catch (InterruptedException e) {
857             fail("Broadcast receiver or NetworkCallback wait was interrupted.");
858         } finally {
859             mCm.unregisterNetworkCallback(pendingIntent);
860             pendingIntent.cancel();
861             mContext.unregisterReceiver(receiver);
862         }
863     }
864 
runIdenticalPendingIntentsRequestTest(boolean useListen)865     private void runIdenticalPendingIntentsRequestTest(boolean useListen) throws Exception {
866         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
867 
868         // Disconnect before registering callbacks, reconnect later to fire them
869         mCtsNetUtils.ensureWifiDisconnected(null);
870 
871         final NetworkRequest firstRequest = makeWifiNetworkRequest();
872         final NetworkRequest secondRequest = new NetworkRequest(firstRequest);
873         // Will match wifi or test, since transports are ORed; but there should only be wifi
874         secondRequest.networkCapabilities.addTransportType(TRANSPORT_TEST);
875 
876         PendingIntent firstIntent = null;
877         PendingIntent secondIntent = null;
878         BroadcastReceiver receiver = null;
879 
880         // Avoid receiving broadcasts from other runs by appending a timestamp
881         final String broadcastAction = NETWORK_CALLBACK_ACTION + System.currentTimeMillis();
882         try {
883             // TODO: replace with PendingIntent.FLAG_MUTABLE when this code compiles against S+
884             // Intent is mutable to receive EXTRA_NETWORK_REQUEST from ConnectivityService
885             final int pendingIntentFlagMutable = 1 << 25;
886             final String extraBoolKey = "extra_bool";
887             firstIntent = PendingIntent.getBroadcast(mContext,
888                     0 /* requestCode */,
889                     new Intent(broadcastAction).putExtra(extraBoolKey, false),
890                     PendingIntent.FLAG_UPDATE_CURRENT | pendingIntentFlagMutable);
891 
892             if (useListen) {
893                 mCm.registerNetworkCallback(firstRequest, firstIntent);
894             } else {
895                 mCm.requestNetwork(firstRequest, firstIntent);
896             }
897 
898             // Second intent equals the first as per filterEquals (extras don't count), so first
899             // intent will be updated with the new extras
900             secondIntent = PendingIntent.getBroadcast(mContext,
901                     0 /* requestCode */,
902                     new Intent(broadcastAction).putExtra(extraBoolKey, true),
903                     PendingIntent.FLAG_UPDATE_CURRENT | pendingIntentFlagMutable);
904 
905             // Because secondIntent.intentFilterEquals the first, the request should be replaced
906             if (useListen) {
907                 mCm.registerNetworkCallback(secondRequest, secondIntent);
908             } else {
909                 mCm.requestNetwork(secondRequest, secondIntent);
910             }
911 
912             final IntentFilter filter = new IntentFilter();
913             filter.addAction(broadcastAction);
914 
915             final CompletableFuture<Network> networkFuture = new CompletableFuture<>();
916             final AtomicInteger receivedCount = new AtomicInteger(0);
917             receiver = new BroadcastReceiver() {
918                 @Override
919                 public void onReceive(Context context, Intent intent) {
920                     final NetworkRequest request = intent.getParcelableExtra(EXTRA_NETWORK_REQUEST);
921                     assertPendingIntentRequestMatches(request, secondRequest, useListen);
922                     receivedCount.incrementAndGet();
923                     networkFuture.complete(intent.getParcelableExtra(EXTRA_NETWORK));
924                 }
925             };
926             mContext.registerReceiver(receiver, filter);
927 
928             final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
929             try {
930                 assertEquals(wifiNetwork, networkFuture.get(
931                         NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
932             } catch (TimeoutException e) {
933                 throw new AssertionError("PendingIntent not received for " + secondRequest, e);
934             }
935 
936             // Sleep for a small amount of time to try to check that only one callback is ever
937             // received (so the first callback was really unregistered). This does not guarantee
938             // that the test will fail if it runs very slowly, but it should at least be very
939             // noticeably flaky.
940             Thread.sleep(NO_CALLBACK_TIMEOUT_MS);
941 
942             // For R- frameworks, listens will receive duplicated callbacks. See b/189868426.
943             if (isAtLeastS() || !useListen) {
944                 assertEquals("PendingIntent should only be received once", 1, receivedCount.get());
945             }
946         } finally {
947             if (firstIntent != null) mCm.unregisterNetworkCallback(firstIntent);
948             if (secondIntent != null) mCm.unregisterNetworkCallback(secondIntent);
949             if (receiver != null) mContext.unregisterReceiver(receiver);
950             mCtsNetUtils.ensureWifiConnected();
951         }
952     }
953 
assertPendingIntentRequestMatches(NetworkRequest broadcasted, NetworkRequest filed, boolean useListen)954     private void assertPendingIntentRequestMatches(NetworkRequest broadcasted, NetworkRequest filed,
955             boolean useListen) {
956         assertArrayEquals(filed.networkCapabilities.getCapabilities(),
957                 broadcasted.networkCapabilities.getCapabilities());
958         // For R- frameworks, listens will receive duplicated callbacks. See b/189868426.
959         if (!isAtLeastS() && useListen) return;
960         assertArrayEquals(filed.networkCapabilities.getTransportTypes(),
961                 broadcasted.networkCapabilities.getTransportTypes());
962     }
963 
964     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
965     @Test
testRegisterNetworkRequest_identicalPendingIntents()966     public void testRegisterNetworkRequest_identicalPendingIntents() throws Exception {
967         runIdenticalPendingIntentsRequestTest(false /* useListen */);
968     }
969 
970     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
971     @Test
testRegisterNetworkCallback_identicalPendingIntents()972     public void testRegisterNetworkCallback_identicalPendingIntents() throws Exception {
973         runIdenticalPendingIntentsRequestTest(true /* useListen */);
974     }
975 
976     /**
977      * Exercises the requestNetwork with NetworkCallback API. This checks to
978      * see if we get a callback for an INTERNET request.
979      */
980     @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
981     @Test
testRequestNetworkCallback()982     public void testRequestNetworkCallback() {
983         final TestNetworkCallback callback = new TestNetworkCallback();
984         mCm.requestNetwork(new NetworkRequest.Builder()
985                 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
986                 .build(), callback);
987 
988         try {
989             // Wait to get callback for availability of internet
990             Network internetNetwork = callback.waitForAvailable();
991             assertNotNull("Did not receive NetworkCallback#onAvailable for INTERNET",
992                     internetNetwork);
993         } catch (InterruptedException e) {
994             fail("NetworkCallback wait was interrupted.");
995         } finally {
996             mCm.unregisterNetworkCallback(callback);
997         }
998     }
999 
1000     /**
1001      * Exercises the requestNetwork with NetworkCallback API with timeout - expected to
1002      * fail. Use WIFI and switch Wi-Fi off.
1003      */
1004     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1005     @Test
testRequestNetworkCallback_onUnavailable()1006     public void testRequestNetworkCallback_onUnavailable() {
1007         final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
1008         if (previousWifiEnabledState) {
1009             mCtsNetUtils.ensureWifiDisconnected(null);
1010         }
1011 
1012         final TestNetworkCallback callback = new TestNetworkCallback();
1013         mCm.requestNetwork(new NetworkRequest.Builder()
1014                 .addTransportType(TRANSPORT_WIFI)
1015                 .build(), callback, 100);
1016 
1017         try {
1018             // Wait to get callback for unavailability of requested network
1019             assertTrue("Did not receive NetworkCallback#onUnavailable",
1020                     callback.waitForUnavailable());
1021         } catch (InterruptedException e) {
1022             fail("NetworkCallback wait was interrupted.");
1023         } finally {
1024             mCm.unregisterNetworkCallback(callback);
1025             if (previousWifiEnabledState) {
1026                 mCtsNetUtils.connectToWifi();
1027             }
1028         }
1029     }
1030 
getFirstV4Address(Network network)1031     private InetAddress getFirstV4Address(Network network) {
1032         LinkProperties linkProperties = mCm.getLinkProperties(network);
1033         for (InetAddress address : linkProperties.getAddresses()) {
1034             if (address instanceof Inet4Address) {
1035                 return address;
1036             }
1037         }
1038         return null;
1039     }
1040 
1041     /**
1042      * Checks that enabling/disabling wifi causes CONNECTIVITY_ACTION broadcasts.
1043      */
1044     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1045     @Test
testToggleWifiConnectivityAction()1046     public void testToggleWifiConnectivityAction() {
1047         // toggleWifi calls connectToWifi and disconnectFromWifi, which both wait for
1048         // CONNECTIVITY_ACTION broadcasts.
1049         mCtsNetUtils.toggleWifi();
1050     }
1051 
1052     /** Verify restricted networks cannot be requested. */
1053     @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
1054     @Test
testRestrictedNetworks()1055     public void testRestrictedNetworks() {
1056         // Verify we can request unrestricted networks:
1057         NetworkRequest request = new NetworkRequest.Builder()
1058                 .addCapability(NET_CAPABILITY_INTERNET).build();
1059         NetworkCallback callback = new NetworkCallback();
1060         mCm.requestNetwork(request, callback);
1061         mCm.unregisterNetworkCallback(callback);
1062         // Verify we cannot request restricted networks:
1063         request = new NetworkRequest.Builder().addCapability(NET_CAPABILITY_IMS).build();
1064         callback = new NetworkCallback();
1065         try {
1066             mCm.requestNetwork(request, callback);
1067             fail("No exception thrown when restricted network requested.");
1068         } catch (SecurityException expected) {}
1069     }
1070 
1071     // Returns "true", "false" or "none"
getWifiMeteredStatus(String ssid)1072     private String getWifiMeteredStatus(String ssid) throws Exception {
1073         // Interestingly giving the SSID as an argument to list wifi-networks
1074         // only works iff the network in question has the "false" policy.
1075         // Also unfortunately runShellCommand does not pass the command to the interpreter
1076         // so it's not possible to | grep the ssid.
1077         final String command = "cmd netpolicy list wifi-networks";
1078         final String policyString = runShellCommand(mInstrumentation, command);
1079 
1080         final Matcher m = Pattern.compile("^" + ssid + ";(true|false|none)$",
1081                 Pattern.MULTILINE | Pattern.UNIX_LINES).matcher(policyString);
1082         if (!m.find()) {
1083             fail("Unexpected format from cmd netpolicy, policyString = " + policyString);
1084         }
1085         return m.group(1);
1086     }
1087 
1088     // metered should be "true", "false" or "none"
setWifiMeteredStatus(String ssid, String metered)1089     private void setWifiMeteredStatus(String ssid, String metered) throws Exception {
1090         final String setCommand = "cmd netpolicy set metered-network " + ssid + " " + metered;
1091         runShellCommand(mInstrumentation, setCommand);
1092         assertEquals(getWifiMeteredStatus(ssid), metered);
1093     }
1094 
unquoteSSID(String ssid)1095     private String unquoteSSID(String ssid) {
1096         // SSID is returned surrounded by quotes if it can be decoded as UTF-8.
1097         // Otherwise it's guaranteed not to start with a quote.
1098         if (ssid.charAt(0) == '"') {
1099             return ssid.substring(1, ssid.length() - 1);
1100         } else {
1101             return ssid;
1102         }
1103     }
1104 
waitForActiveNetworkMetered(final int targetTransportType, final boolean requestedMeteredness, final boolean waitForValidation, final boolean useSystemDefault)1105     private Network waitForActiveNetworkMetered(final int targetTransportType,
1106             final boolean requestedMeteredness, final boolean waitForValidation,
1107             final boolean useSystemDefault)
1108             throws Exception {
1109         final CompletableFuture<Network> networkFuture = new CompletableFuture<>();
1110         final NetworkCallback networkCallback = new NetworkCallback() {
1111             @Override
1112             public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
1113                 if (!nc.hasTransport(targetTransportType)) return;
1114 
1115                 final boolean metered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
1116                 final boolean validated = nc.hasCapability(NET_CAPABILITY_VALIDATED);
1117                 if (metered == requestedMeteredness && (!waitForValidation || validated)) {
1118                     networkFuture.complete(network);
1119                 }
1120             }
1121         };
1122 
1123         try {
1124             // Registering a callback here guarantees onCapabilitiesChanged is called immediately
1125             // with the current setting. Therefore, if the setting has already been changed,
1126             // this method will return right away, and if not, it'll wait for the setting to change.
1127             if (useSystemDefault) {
1128                 runWithShellPermissionIdentity(() ->
1129                                 mCmShim.registerSystemDefaultNetworkCallback(networkCallback,
1130                                         new Handler(Looper.getMainLooper())),
1131                         NETWORK_SETTINGS);
1132             } else {
1133                 mCm.registerDefaultNetworkCallback(networkCallback);
1134             }
1135 
1136             // Changing meteredness on wifi involves reconnecting, which can take several seconds
1137             // (involves re-associating, DHCP...).
1138             return networkFuture.get(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
1139         } catch (TimeoutException e) {
1140             throw new AssertionError("Timed out waiting for active network metered status to "
1141                     + "change to " + requestedMeteredness + " ; network = "
1142                     + mCm.getActiveNetwork(), e);
1143         } finally {
1144             mCm.unregisterNetworkCallback(networkCallback);
1145         }
1146     }
1147 
setWifiMeteredStatusAndWait(String ssid, boolean isMetered, boolean waitForValidation)1148     private Network setWifiMeteredStatusAndWait(String ssid, boolean isMetered,
1149             boolean waitForValidation) throws Exception {
1150         setWifiMeteredStatus(ssid, Boolean.toString(isMetered) /* metered */);
1151         mCtsNetUtils.ensureWifiConnected();
1152         return waitForActiveNetworkMetered(TRANSPORT_WIFI,
1153                 isMetered /* requestedMeteredness */,
1154                 waitForValidation,
1155                 true /* useSystemDefault */);
1156     }
1157 
assertMultipathPreferenceIsEventually(Network network, int oldValue, int expectedValue)1158     private void assertMultipathPreferenceIsEventually(Network network, int oldValue,
1159             int expectedValue) {
1160         // Quick check : if oldValue == expectedValue, there is no way to guarantee the test
1161         // is not flaky.
1162         assertNotSame(oldValue, expectedValue);
1163 
1164         for (int i = 0; i < NUM_TRIES_MULTIPATH_PREF_CHECK; ++i) {
1165             final int actualValue = mCm.getMultipathPreference(network);
1166             if (actualValue == expectedValue) {
1167                 return;
1168             }
1169             if (actualValue != oldValue) {
1170                 fail("Multipath preference is neither previous (" + oldValue
1171                         + ") nor expected (" + expectedValue + ")");
1172             }
1173             SystemClock.sleep(INTERVAL_MULTIPATH_PREF_CHECK_MS);
1174         }
1175         fail("Timed out waiting for multipath preference to change. expected = "
1176                 + expectedValue + " ; actual = " + mCm.getMultipathPreference(network));
1177     }
1178 
getCurrentMeteredMultipathPreference(ContentResolver resolver)1179     private int getCurrentMeteredMultipathPreference(ContentResolver resolver) {
1180         final String rawMeteredPref = Settings.Global.getString(resolver,
1181                 NETWORK_METERED_MULTIPATH_PREFERENCE);
1182         return TextUtils.isEmpty(rawMeteredPref)
1183             ? getIntResourceForName(NETWORK_METERED_MULTIPATH_PREFERENCE_RES_NAME)
1184             : Integer.parseInt(rawMeteredPref);
1185     }
1186 
findNextPrefValue(ContentResolver resolver)1187     private int findNextPrefValue(ContentResolver resolver) {
1188         // A bit of a nuclear hammer, but race conditions in CTS are bad. To be able to
1189         // detect a correct setting value without race conditions, the next pref must
1190         // be a valid value (range 0..3) that is different from the old setting of the
1191         // metered preference and from the unmetered preference.
1192         final int meteredPref = getCurrentMeteredMultipathPreference(resolver);
1193         final int unmeteredPref = ConnectivityManager.MULTIPATH_PREFERENCE_UNMETERED;
1194         if (0 != meteredPref && 0 != unmeteredPref) return 0;
1195         if (1 != meteredPref && 1 != unmeteredPref) return 1;
1196         return 2;
1197     }
1198 
1199     /**
1200      * Verify that getMultipathPreference does return appropriate values
1201      * for metered and unmetered networks.
1202      */
1203     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1204     @Test
testGetMultipathPreference()1205     public void testGetMultipathPreference() throws Exception {
1206         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
1207         final ContentResolver resolver = mContext.getContentResolver();
1208         mCtsNetUtils.ensureWifiConnected();
1209         final String ssid = unquoteSSID(mWifiManager.getConnectionInfo().getSSID());
1210         final String oldMeteredSetting = getWifiMeteredStatus(ssid);
1211         final String oldMeteredMultipathPreference = Settings.Global.getString(
1212                 resolver, NETWORK_METERED_MULTIPATH_PREFERENCE);
1213         try {
1214             final int initialMeteredPreference = getCurrentMeteredMultipathPreference(resolver);
1215             int newMeteredPreference = findNextPrefValue(resolver);
1216             Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
1217                     Integer.toString(newMeteredPreference));
1218             // Wifi meteredness changes from unmetered to metered will disconnect and reconnect
1219             // since R.
1220             final Network network = setWifiMeteredStatusAndWait(ssid, true /* isMetered */,
1221                     false /* waitForValidation */);
1222             assertEquals(ssid, unquoteSSID(mWifiManager.getConnectionInfo().getSSID()));
1223             assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
1224                     NET_CAPABILITY_NOT_METERED), false);
1225             assertMultipathPreferenceIsEventually(network, initialMeteredPreference,
1226                     newMeteredPreference);
1227 
1228             final int oldMeteredPreference = newMeteredPreference;
1229             newMeteredPreference = findNextPrefValue(resolver);
1230             Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
1231                     Integer.toString(newMeteredPreference));
1232             assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
1233                     NET_CAPABILITY_NOT_METERED), false);
1234             assertMultipathPreferenceIsEventually(network,
1235                     oldMeteredPreference, newMeteredPreference);
1236 
1237             // No disconnect from unmetered to metered.
1238             setWifiMeteredStatusAndWait(ssid, false /* isMetered */, false /* waitForValidation */);
1239             assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
1240                     NET_CAPABILITY_NOT_METERED), true);
1241             assertMultipathPreferenceIsEventually(network, newMeteredPreference,
1242                     ConnectivityManager.MULTIPATH_PREFERENCE_UNMETERED);
1243         } finally {
1244             Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
1245                     oldMeteredMultipathPreference);
1246             setWifiMeteredStatus(ssid, oldMeteredSetting);
1247         }
1248     }
1249 
1250     // TODO: move the following socket keep alive test to dedicated test class.
1251     /**
1252      * Callback used in tcp keepalive offload that allows caller to wait callback fires.
1253      */
1254     private static class TestSocketKeepaliveCallback extends SocketKeepalive.Callback {
1255         public enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
1256 
1257         public static class CallbackValue {
1258             public final CallbackType callbackType;
1259             public final int error;
1260 
CallbackValue(final CallbackType type, final int error)1261             private CallbackValue(final CallbackType type, final int error) {
1262                 this.callbackType = type;
1263                 this.error = error;
1264             }
1265 
1266             public static class OnStartedCallback extends CallbackValue {
OnStartedCallback()1267                 OnStartedCallback() { super(CallbackType.ON_STARTED, 0); }
1268             }
1269 
1270             public static class OnStoppedCallback extends CallbackValue {
OnStoppedCallback()1271                 OnStoppedCallback() { super(CallbackType.ON_STOPPED, 0); }
1272             }
1273 
1274             public static class OnErrorCallback extends CallbackValue {
OnErrorCallback(final int error)1275                 OnErrorCallback(final int error) { super(CallbackType.ON_ERROR, error); }
1276             }
1277 
1278             @Override
equals(Object o)1279             public boolean equals(Object o) {
1280                 return o.getClass() == this.getClass()
1281                         && this.callbackType == ((CallbackValue) o).callbackType
1282                         && this.error == ((CallbackValue) o).error;
1283             }
1284 
1285             @Override
toString()1286             public String toString() {
1287                 return String.format("%s(%s, %d)", getClass().getSimpleName(), callbackType, error);
1288             }
1289         }
1290 
1291         private final LinkedBlockingQueue<CallbackValue> mCallbacks = new LinkedBlockingQueue<>();
1292 
1293         @Override
onStarted()1294         public void onStarted() {
1295             mCallbacks.add(new CallbackValue.OnStartedCallback());
1296         }
1297 
1298         @Override
onStopped()1299         public void onStopped() {
1300             mCallbacks.add(new CallbackValue.OnStoppedCallback());
1301         }
1302 
1303         @Override
onError(final int error)1304         public void onError(final int error) {
1305             mCallbacks.add(new CallbackValue.OnErrorCallback(error));
1306         }
1307 
pollCallback()1308         public CallbackValue pollCallback() {
1309             try {
1310                 return mCallbacks.poll(KEEPALIVE_CALLBACK_TIMEOUT_MS,
1311                         TimeUnit.MILLISECONDS);
1312             } catch (InterruptedException e) {
1313                 fail("Callback not seen after " + KEEPALIVE_CALLBACK_TIMEOUT_MS + " ms");
1314             }
1315             return null;
1316         }
expectCallback(CallbackValue expectedCallback)1317         private void expectCallback(CallbackValue expectedCallback) {
1318             final CallbackValue actualCallback = pollCallback();
1319             assertEquals(expectedCallback, actualCallback);
1320         }
1321 
expectStarted()1322         public void expectStarted() {
1323             expectCallback(new CallbackValue.OnStartedCallback());
1324         }
1325 
expectStopped()1326         public void expectStopped() {
1327             expectCallback(new CallbackValue.OnStoppedCallback());
1328         }
1329 
expectError(int error)1330         public void expectError(int error) {
1331             expectCallback(new CallbackValue.OnErrorCallback(error));
1332         }
1333     }
1334 
getAddrByName(final String hostname, final int family)1335     private InetAddress getAddrByName(final String hostname, final int family) throws Exception {
1336         final InetAddress[] allAddrs = InetAddress.getAllByName(hostname);
1337         for (InetAddress addr : allAddrs) {
1338             if (family == AF_INET && addr instanceof Inet4Address) return addr;
1339 
1340             if (family == AF_INET6 && addr instanceof Inet6Address) return addr;
1341 
1342             if (family == AF_UNSPEC) return addr;
1343         }
1344         return null;
1345     }
1346 
getConnectedSocket(final Network network, final String host, final int port, final int family)1347     private Socket getConnectedSocket(final Network network, final String host, final int port,
1348             final int family) throws Exception {
1349         final Socket s = network.getSocketFactory().createSocket();
1350         try {
1351             final InetAddress addr = getAddrByName(host, family);
1352             if (addr == null) fail("Fail to get destination address for " + family);
1353 
1354             final InetSocketAddress sockAddr = new InetSocketAddress(addr, port);
1355             s.connect(sockAddr);
1356         } catch (Exception e) {
1357             s.close();
1358             throw e;
1359         }
1360         return s;
1361     }
1362 
getSupportedKeepalivesForNet(@onNull Network network)1363     private int getSupportedKeepalivesForNet(@NonNull Network network) throws Exception {
1364         final NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
1365 
1366         // Get number of supported concurrent keepalives for testing network.
1367         final int[] keepalivesPerTransport = KeepaliveUtils.getSupportedKeepalives(mContext);
1368         return KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(
1369                 keepalivesPerTransport, nc);
1370     }
1371 
isTcpKeepaliveSupportedByKernel()1372     private static boolean isTcpKeepaliveSupportedByKernel() {
1373         final String kVersionString = VintfRuntimeInfo.getKernelVersion();
1374         return compareMajorMinorVersion(kVersionString, "4.8") >= 0;
1375     }
1376 
getVersionFromString(String version)1377     private static Pair<Integer, Integer> getVersionFromString(String version) {
1378         // Only gets major and minor number of the version string.
1379         final Pattern versionPattern = Pattern.compile("^(\\d+)(\\.(\\d+))?.*");
1380         final Matcher m = versionPattern.matcher(version);
1381         if (m.matches()) {
1382             final int major = Integer.parseInt(m.group(1));
1383             final int minor = TextUtils.isEmpty(m.group(3)) ? 0 : Integer.parseInt(m.group(3));
1384             return new Pair<>(major, minor);
1385         } else {
1386             return new Pair<>(0, 0);
1387         }
1388     }
1389 
1390     // TODO: Move to util class.
compareMajorMinorVersion(final String s1, final String s2)1391     private static int compareMajorMinorVersion(final String s1, final String s2) {
1392         final Pair<Integer, Integer> v1 = getVersionFromString(s1);
1393         final Pair<Integer, Integer> v2 = getVersionFromString(s2);
1394 
1395         if (v1.first == v2.first) {
1396             return Integer.compare(v1.second, v2.second);
1397         } else {
1398             return Integer.compare(v1.first, v2.first);
1399         }
1400     }
1401 
1402     /**
1403      * Verifies that version string compare logic returns expected result for various cases.
1404      * Note that only major and minor number are compared.
1405      */
1406     @Test
testMajorMinorVersionCompare()1407     public void testMajorMinorVersionCompare() {
1408         assertEquals(0, compareMajorMinorVersion("4.8.1", "4.8"));
1409         assertEquals(1, compareMajorMinorVersion("4.9", "4.8.1"));
1410         assertEquals(1, compareMajorMinorVersion("5.0", "4.8"));
1411         assertEquals(1, compareMajorMinorVersion("5", "4.8"));
1412         assertEquals(0, compareMajorMinorVersion("5", "5.0"));
1413         assertEquals(1, compareMajorMinorVersion("5-beta1", "4.8"));
1414         assertEquals(0, compareMajorMinorVersion("4.8.0.0", "4.8"));
1415         assertEquals(0, compareMajorMinorVersion("4.8-RC1", "4.8"));
1416         assertEquals(0, compareMajorMinorVersion("4.8", "4.8"));
1417         assertEquals(-1, compareMajorMinorVersion("3.10", "4.8.0"));
1418         assertEquals(-1, compareMajorMinorVersion("4.7.10.10", "4.8"));
1419     }
1420 
1421     /**
1422      * Verifies that the keepalive API cannot create any keepalive when the maximum number of
1423      * keepalives is set to 0.
1424      */
1425     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1426     @Test
testKeepaliveWifiUnsupported()1427     public void testKeepaliveWifiUnsupported() throws Exception {
1428         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
1429 
1430         final Network network = mCtsNetUtils.ensureWifiConnected();
1431         if (getSupportedKeepalivesForNet(network) != 0) return;
1432         final InetAddress srcAddr = getFirstV4Address(network);
1433         assumeTrue("This test requires native IPv4", srcAddr != null);
1434 
1435         runWithShellPermissionIdentity(() -> {
1436             assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 1, 0));
1437             assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 0, 1));
1438         });
1439     }
1440 
1441     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1442     @Test
1443     @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
testCreateTcpKeepalive()1444     public void testCreateTcpKeepalive() throws Exception {
1445         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
1446 
1447         final Network network = mCtsNetUtils.ensureWifiConnected();
1448         if (getSupportedKeepalivesForNet(network) == 0) return;
1449         final InetAddress srcAddr = getFirstV4Address(network);
1450         assumeTrue("This test requires native IPv4", srcAddr != null);
1451 
1452         // If kernel < 4.8 then it doesn't support TCP keepalive, but it might still support
1453         // NAT-T keepalive. If keepalive limits from resource overlay is not zero, TCP keepalive
1454         // needs to be supported except if the kernel doesn't support it.
1455         if (!isTcpKeepaliveSupportedByKernel()) {
1456             // Verify that the callback result is expected.
1457             runWithShellPermissionIdentity(() -> {
1458                 assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 0, 1));
1459             });
1460             Log.i(TAG, "testCreateTcpKeepalive is skipped for kernel "
1461                     + VintfRuntimeInfo.getKernelVersion());
1462             return;
1463         }
1464 
1465         final byte[] requestBytes = CtsNetUtils.HTTP_REQUEST.getBytes("UTF-8");
1466         // So far only ipv4 tcp keepalive offload is supported.
1467         // TODO: add test case for ipv6 tcp keepalive offload when it is supported.
1468         try (Socket s = getConnectedSocket(network, TEST_HOST, HTTP_PORT, AF_INET)) {
1469 
1470             // Should able to start keep alive offload when socket is idle.
1471             final Executor executor = mContext.getMainExecutor();
1472             final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
1473 
1474             mUiAutomation.adoptShellPermissionIdentity();
1475             try (SocketKeepalive sk = mCm.createSocketKeepalive(network, s, executor, callback)) {
1476                 sk.start(MIN_KEEPALIVE_INTERVAL);
1477                 callback.expectStarted();
1478 
1479                 // App should not able to write during keepalive offload.
1480                 final OutputStream out = s.getOutputStream();
1481                 try {
1482                     out.write(requestBytes);
1483                     fail("Should not able to write");
1484                 } catch (IOException e) { }
1485                 // App should not able to read during keepalive offload.
1486                 final InputStream in = s.getInputStream();
1487                 byte[] responseBytes = new byte[4096];
1488                 try {
1489                     in.read(responseBytes);
1490                     fail("Should not able to read");
1491                 } catch (IOException e) { }
1492 
1493                 // Stop.
1494                 sk.stop();
1495                 callback.expectStopped();
1496             } finally {
1497                 mUiAutomation.dropShellPermissionIdentity();
1498             }
1499 
1500             // Ensure socket is still connected.
1501             assertTrue(s.isConnected());
1502             assertFalse(s.isClosed());
1503 
1504             // Let socket be not idle.
1505             try {
1506                 final OutputStream out = s.getOutputStream();
1507                 out.write(requestBytes);
1508             } catch (IOException e) {
1509                 fail("Failed to write data " + e);
1510             }
1511             // Make sure response data arrives.
1512             final MessageQueue fdHandlerQueue = Looper.getMainLooper().getQueue();
1513             final FileDescriptor fd = s.getFileDescriptor$();
1514             final CountDownLatch mOnReceiveLatch = new CountDownLatch(1);
1515             fdHandlerQueue.addOnFileDescriptorEventListener(fd, EVENT_INPUT, (readyFd, events) -> {
1516                 mOnReceiveLatch.countDown();
1517                 return 0; // Unregister listener.
1518             });
1519             if (!mOnReceiveLatch.await(2, TimeUnit.SECONDS)) {
1520                 fdHandlerQueue.removeOnFileDescriptorEventListener(fd);
1521                 fail("Timeout: no response data");
1522             }
1523 
1524             // Should get ERROR_SOCKET_NOT_IDLE because there is still data in the receive queue
1525             // that has not been read.
1526             mUiAutomation.adoptShellPermissionIdentity();
1527             try (SocketKeepalive sk = mCm.createSocketKeepalive(network, s, executor, callback)) {
1528                 sk.start(MIN_KEEPALIVE_INTERVAL);
1529                 callback.expectError(SocketKeepalive.ERROR_SOCKET_NOT_IDLE);
1530             } finally {
1531                 mUiAutomation.dropShellPermissionIdentity();
1532             }
1533         }
1534     }
1535 
createConcurrentKeepalivesOfType( int requestCount, @NonNull TestSocketKeepaliveCallback callback, Supplier<SocketKeepalive> kaFactory)1536     private ArrayList<SocketKeepalive> createConcurrentKeepalivesOfType(
1537             int requestCount, @NonNull TestSocketKeepaliveCallback callback,
1538             Supplier<SocketKeepalive> kaFactory) {
1539         final ArrayList<SocketKeepalive> kalist = new ArrayList<>();
1540 
1541         int remainingRetries = MAX_KEEPALIVE_RETRY_COUNT;
1542 
1543         // Test concurrent keepalives with the given supplier.
1544         while (kalist.size() < requestCount) {
1545             final SocketKeepalive ka = kaFactory.get();
1546             ka.start(MIN_KEEPALIVE_INTERVAL);
1547             TestSocketKeepaliveCallback.CallbackValue cv = callback.pollCallback();
1548             assertNotNull(cv);
1549             if (cv.callbackType == TestSocketKeepaliveCallback.CallbackType.ON_ERROR) {
1550                 if (kalist.size() == 0 && cv.error == SocketKeepalive.ERROR_UNSUPPORTED) {
1551                     // Unsupported.
1552                     break;
1553                 } else if (cv.error == SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES) {
1554                     // Limit reached or temporary unavailable due to stopped slot is not yet
1555                     // released.
1556                     if (remainingRetries > 0) {
1557                         SystemClock.sleep(INTERVAL_KEEPALIVE_RETRY_MS);
1558                         remainingRetries--;
1559                         continue;
1560                     }
1561                     break;
1562                 }
1563             }
1564             if (cv.callbackType == TestSocketKeepaliveCallback.CallbackType.ON_STARTED) {
1565                 kalist.add(ka);
1566             } else {
1567                 fail("Unexpected error when creating " + (kalist.size() + 1) + " "
1568                         + ka.getClass().getSimpleName() + ": " + cv);
1569             }
1570         }
1571 
1572         return kalist;
1573     }
1574 
createConcurrentNattSocketKeepalives( @onNull Network network, @NonNull InetAddress srcAddr, int requestCount, @NonNull TestSocketKeepaliveCallback callback)1575     private @NonNull ArrayList<SocketKeepalive> createConcurrentNattSocketKeepalives(
1576             @NonNull Network network, @NonNull InetAddress srcAddr, int requestCount,
1577             @NonNull TestSocketKeepaliveCallback callback)  throws Exception {
1578 
1579         final Executor executor = mContext.getMainExecutor();
1580 
1581         // Initialize a real NaT-T socket.
1582         final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
1583         final UdpEncapsulationSocket nattSocket = mIpSec.openUdpEncapsulationSocket();
1584         final InetAddress dstAddr = getAddrByName(TEST_HOST, AF_INET);
1585         assertNotNull(srcAddr);
1586         assertNotNull(dstAddr);
1587 
1588         // Test concurrent Nat-T keepalives.
1589         final ArrayList<SocketKeepalive> result = createConcurrentKeepalivesOfType(requestCount,
1590                 callback, () -> mCm.createSocketKeepalive(network, nattSocket,
1591                         srcAddr, dstAddr, executor, callback));
1592 
1593         nattSocket.close();
1594         return result;
1595     }
1596 
createConcurrentTcpSocketKeepalives( @onNull Network network, int requestCount, @NonNull TestSocketKeepaliveCallback callback)1597     private @NonNull ArrayList<SocketKeepalive> createConcurrentTcpSocketKeepalives(
1598             @NonNull Network network, int requestCount,
1599             @NonNull TestSocketKeepaliveCallback callback) {
1600         final Executor executor = mContext.getMainExecutor();
1601 
1602         // Create concurrent TCP keepalives.
1603         return createConcurrentKeepalivesOfType(requestCount, callback, () -> {
1604             // Assert that TCP connections can be established. The file descriptor of tcp
1605             // sockets will be duplicated and kept valid in service side if the keepalives are
1606             // successfully started.
1607             try (Socket tcpSocket = getConnectedSocket(network, TEST_HOST, HTTP_PORT,
1608                     AF_INET)) {
1609                 return mCm.createSocketKeepalive(network, tcpSocket, executor, callback);
1610             } catch (Exception e) {
1611                 fail("Unexpected error when creating TCP socket: " + e);
1612             }
1613             return null;
1614         });
1615     }
1616 
1617     /**
1618      * Creates concurrent keepalives until the specified counts of each type of keepalives are
1619      * reached or the expected error callbacks are received for each type of keepalives.
1620      *
1621      * @return the total number of keepalives created.
1622      */
1623     private int createConcurrentSocketKeepalives(
1624             @NonNull Network network, @NonNull InetAddress srcAddr, int nattCount, int tcpCount)
1625             throws Exception {
1626         final ArrayList<SocketKeepalive> kalist = new ArrayList<>();
1627         final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
1628 
1629         kalist.addAll(createConcurrentNattSocketKeepalives(network, srcAddr, nattCount, callback));
1630         kalist.addAll(createConcurrentTcpSocketKeepalives(network, tcpCount, callback));
1631 
1632         final int ret = kalist.size();
1633 
1634         // Clean up.
1635         for (final SocketKeepalive ka : kalist) {
1636             ka.stop();
1637             callback.expectStopped();
1638         }
1639         kalist.clear();
1640 
1641         return ret;
1642     }
1643 
1644     /**
1645      * Verifies that the concurrent keepalive slots meet the minimum requirement, and don't
1646      * get leaked after iterations.
1647      */
1648     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1649     @Test
1650     @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
1651     public void testSocketKeepaliveLimitWifi() throws Exception {
1652         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
1653 
1654         final Network network = mCtsNetUtils.ensureWifiConnected();
1655         final int supported = getSupportedKeepalivesForNet(network);
1656         if (supported == 0) {
1657             return;
1658         }
1659         final InetAddress srcAddr = getFirstV4Address(network);
1660         assumeTrue("This test requires native IPv4", srcAddr != null);
1661 
1662         runWithShellPermissionIdentity(() -> {
1663             // Verifies that the supported keepalive slots meet MIN_SUPPORTED_KEEPALIVE_COUNT.
1664             assertGreaterOrEqual(supported, MIN_SUPPORTED_WIFI_KEEPALIVE_COUNT);
1665 
1666             // Verifies that Nat-T keepalives can be established.
1667             assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
1668                     supported + 1, 0));
1669             // Verifies that keepalives don't get leaked in second round.
1670             assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, supported,
1671                     0));
1672         });
1673 
1674         // If kernel < 4.8 then it doesn't support TCP keepalive, but it might still support
1675         // NAT-T keepalive. Test below cases only if TCP keepalive is supported by kernel.
1676         if (!isTcpKeepaliveSupportedByKernel()) return;
1677 
1678         runWithShellPermissionIdentity(() -> {
1679             assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, 0,
1680                     supported + 1));
1681 
1682             // Verifies that different types can be established at the same time.
1683             assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
1684                     supported / 2, supported - supported / 2));
1685 
1686             // Verifies that keepalives don't get leaked in second round.
1687             assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, 0,
1688                     supported));
1689             assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
1690                     supported / 2, supported - supported / 2));
1691         });
1692     }
1693 
1694     /**
1695      * Verifies that the concurrent keepalive slots meet the minimum telephony requirement, and
1696      * don't get leaked after iterations.
1697      */
1698     @AppModeFull(reason = "Cannot request network in instant app mode")
1699     @Test
1700     @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
1701     public void testSocketKeepaliveLimitTelephony() throws Exception {
1702         if (!mPackageManager.hasSystemFeature(FEATURE_TELEPHONY)) {
1703             Log.i(TAG, "testSocketKeepaliveLimitTelephony cannot execute unless device"
1704                     + " supports telephony");
1705             return;
1706         }
1707 
1708         final int firstSdk = SdkLevel.isAtLeastS()
1709                 ? Build.VERSION.DEVICE_INITIAL_SDK_INT
1710                 // FIRST_SDK_INT was a @TestApi field renamed to DEVICE_INITIAL_SDK_INT in S
1711                 : Build.VERSION.class.getField("FIRST_SDK_INT").getInt(null);
1712         if (firstSdk < Build.VERSION_CODES.Q) {
1713             Log.i(TAG, "testSocketKeepaliveLimitTelephony: skip test for devices launching"
1714                     + " before Q: " + firstSdk);
1715             return;
1716         }
1717 
1718         final Network network = mCtsNetUtils.connectToCell();
1719         final int supported = getSupportedKeepalivesForNet(network);
1720         final InetAddress srcAddr = getFirstV4Address(network);
1721         assumeTrue("This test requires native IPv4", srcAddr != null);
1722 
1723         runWithShellPermissionIdentity(() -> {
1724             // Verifies that the supported keepalive slots meet minimum requirement.
1725             assertGreaterOrEqual(supported, MIN_SUPPORTED_CELLULAR_KEEPALIVE_COUNT);
1726             // Verifies that Nat-T keepalives can be established.
1727             assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
1728                     supported + 1, 0));
1729             // Verifies that keepalives don't get leaked in second round.
1730             assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, supported,
1731                     0));
1732         });
1733     }
1734 
1735     private int getIntResourceForName(@NonNull String resName) {
1736         final Resources r = mContext.getResources();
1737         final int resId = r.getIdentifier(resName, "integer", "android");
1738         return r.getInteger(resId);
1739     }
1740 
1741     /**
1742      * Verifies that the keepalive slots are limited as customized for unprivileged requests.
1743      */
1744     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1745     @Test
1746     @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
1747     public void testSocketKeepaliveUnprivileged() throws Exception {
1748         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
1749 
1750         final Network network = mCtsNetUtils.ensureWifiConnected();
1751         final int supported = getSupportedKeepalivesForNet(network);
1752         if (supported == 0) {
1753             return;
1754         }
1755         final InetAddress srcAddr = getFirstV4Address(network);
1756         assumeTrue("This test requires native IPv4", srcAddr != null);
1757 
1758         // Resource ID might be shifted on devices that compiled with different symbols.
1759         // Thus, resolve ID at runtime is needed.
1760         final int allowedUnprivilegedPerUid =
1761                 getIntResourceForName(KEEPALIVE_ALLOWED_UNPRIVILEGED_RES_NAME);
1762         final int reservedPrivilegedSlots =
1763                 getIntResourceForName(KEEPALIVE_RESERVED_PER_SLOT_RES_NAME);
1764         // Verifies that unprivileged request per uid cannot exceed the limit customized in the
1765         // resource. Currently, unprivileged keepalive slots are limited to Nat-T only, this test
1766         // does not apply to TCP.
1767         assertGreaterOrEqual(supported, reservedPrivilegedSlots);
1768         assertGreaterOrEqual(supported, allowedUnprivilegedPerUid);
1769         final int expectedUnprivileged =
1770                 Math.min(allowedUnprivilegedPerUid, supported - reservedPrivilegedSlots);
1771         assertEquals(expectedUnprivileged,
1772                 createConcurrentSocketKeepalives(network, srcAddr, supported + 1, 0));
1773     }
1774 
1775     private static void assertGreaterOrEqual(long greater, long lesser) {
1776         assertTrue("" + greater + " expected to be greater than or equal to " + lesser,
1777                 greater >= lesser);
1778     }
1779 
1780     /**
1781      * Verifies that apps are not allowed to access restricted networks even if they declare the
1782      * CONNECTIVITY_USE_RESTRICTED_NETWORKS permission in their manifests.
1783      * See. b/144679405.
1784      */
1785     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1786     @Test
1787     public void testRestrictedNetworkPermission() throws Exception {
1788         // Ensure that CONNECTIVITY_USE_RESTRICTED_NETWORKS isn't granted to this package.
1789         final PackageInfo app = mPackageManager.getPackageInfo(mContext.getPackageName(),
1790                 GET_PERMISSIONS);
1791         final int index = ArrayUtils.indexOf(
1792                 app.requestedPermissions, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
1793         assertTrue(index >= 0);
1794         assertTrue(app.requestedPermissionsFlags[index] != PERMISSION_GRANTED);
1795 
1796         // Ensure that NetworkUtils.queryUserAccess always returns false since this package should
1797         // not have netd system permission to call this function.
1798         final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
1799         assertFalse(NetworkUtils.queryUserAccess(Binder.getCallingUid(), wifiNetwork.netId));
1800 
1801         // Ensure that this package cannot bind to any restricted network that's currently
1802         // connected.
1803         Network[] networks = mCm.getAllNetworks();
1804         for (Network network : networks) {
1805             NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
1806             if (nc != null && !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
1807                 try {
1808                     network.bindSocket(new Socket());
1809                     fail("Bind to restricted network " + network + " unexpectedly succeeded");
1810                 } catch (IOException expected) {}
1811             }
1812         }
1813     }
1814 
1815     /**
1816      * Verifies that apps are allowed to call setAirplaneMode if they declare
1817      * NETWORK_AIRPLANE_MODE permission in their manifests.
1818      * See b/145164696.
1819      */
1820     @AppModeFull(reason = "NETWORK_AIRPLANE_MODE permission can't be granted to instant apps")
1821     @Test
1822     public void testSetAirplaneMode() throws Exception{
1823         final boolean supportWifi = mPackageManager.hasSystemFeature(FEATURE_WIFI);
1824         final boolean supportTelephony = mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
1825         // store the current state of airplane mode
1826         final boolean isAirplaneModeEnabled = isAirplaneModeEnabled();
1827         final TestableNetworkCallback wifiCb = new TestableNetworkCallback();
1828         final TestableNetworkCallback telephonyCb = new TestableNetworkCallback();
1829         // disable airplane mode to reach a known state
1830         runShellCommand("cmd connectivity airplane-mode disable");
1831         // Verify that networks are available as expected if wifi or cell is supported. Continue the
1832         // test if none of them are supported since test should still able to verify the permission
1833         // mechanism.
1834         if (supportWifi) requestAndWaitForAvailable(makeWifiNetworkRequest(), wifiCb);
1835         if (supportTelephony) requestAndWaitForAvailable(makeCellNetworkRequest(), telephonyCb);
1836 
1837         try {
1838             // Verify we cannot set Airplane Mode without correct permission:
1839             try {
1840                 setAndVerifyAirplaneMode(true);
1841                 fail("SecurityException should have been thrown when setAirplaneMode was called"
1842                         + "without holding permission NETWORK_AIRPLANE_MODE.");
1843             } catch (SecurityException expected) {}
1844 
1845             // disable airplane mode again to reach a known state
1846             runShellCommand("cmd connectivity airplane-mode disable");
1847 
1848             // adopt shell permission which holds NETWORK_AIRPLANE_MODE
1849             mUiAutomation.adoptShellPermissionIdentity();
1850 
1851             // Verify we can enable Airplane Mode with correct permission:
1852             try {
1853                 setAndVerifyAirplaneMode(true);
1854             } catch (SecurityException e) {
1855                 fail("SecurityException should not have been thrown when setAirplaneMode(true) was"
1856                         + "called whilst holding the NETWORK_AIRPLANE_MODE permission.");
1857             }
1858             // Verify that the enabling airplane mode takes effect as expected to prevent flakiness
1859             // caused by fast airplane mode switches. Ensure network lost before turning off
1860             // airplane mode.
1861             if (supportWifi) waitForLost(wifiCb);
1862             if (supportTelephony) waitForLost(telephonyCb);
1863 
1864             // Verify we can disable Airplane Mode with correct permission:
1865             try {
1866                 setAndVerifyAirplaneMode(false);
1867             } catch (SecurityException e) {
1868                 fail("SecurityException should not have been thrown when setAirplaneMode(false) was"
1869                         + "called whilst holding the NETWORK_AIRPLANE_MODE permission.");
1870             }
1871             // Verify that turning airplane mode off takes effect as expected.
1872             if (supportWifi) waitForAvailable(wifiCb);
1873             if (supportTelephony) waitForAvailable(telephonyCb);
1874         } finally {
1875             if (supportWifi) mCm.unregisterNetworkCallback(wifiCb);
1876             if (supportTelephony) mCm.unregisterNetworkCallback(telephonyCb);
1877             // Restore the previous state of airplane mode and permissions:
1878             runShellCommand("cmd connectivity airplane-mode "
1879                     + (isAirplaneModeEnabled ? "enable" : "disable"));
1880             mUiAutomation.dropShellPermissionIdentity();
1881         }
1882     }
1883 
1884     private void requestAndWaitForAvailable(@NonNull final NetworkRequest request,
1885             @NonNull final TestableNetworkCallback cb) {
1886         mCm.registerNetworkCallback(request, cb);
1887         waitForAvailable(cb);
1888     }
1889 
1890     private void waitForAvailable(@NonNull final TestableNetworkCallback cb) {
1891         cb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
1892                 c -> c instanceof CallbackEntry.Available);
1893     }
1894 
1895     private void waitForAvailable(
1896             @NonNull final TestableNetworkCallback cb, final int expectedTransport) {
1897         cb.eventuallyExpect(
1898                 CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
1899                 entry -> {
1900                     final NetworkCapabilities nc = mCm.getNetworkCapabilities(entry.getNetwork());
1901                     return nc.hasTransport(expectedTransport);
1902                 }
1903         );
1904     }
1905 
1906     private void waitForAvailable(
1907             @NonNull final TestableNetworkCallback cb, @NonNull final Network expectedNetwork) {
1908         cb.expectAvailableCallbacks(expectedNetwork, false /* suspended */,
1909                 true /* validated */,
1910                 false /* blocked */, NETWORK_CALLBACK_TIMEOUT_MS);
1911     }
1912 
1913     private void waitForLost(@NonNull final TestableNetworkCallback cb) {
1914         cb.eventuallyExpect(CallbackEntry.LOST, NETWORK_CALLBACK_TIMEOUT_MS,
1915                 c -> c instanceof CallbackEntry.Lost);
1916     }
1917 
1918     private void setAndVerifyAirplaneMode(Boolean expectedResult)
1919             throws Exception {
1920         final CompletableFuture<Boolean> actualResult = new CompletableFuture();
1921         BroadcastReceiver receiver = new BroadcastReceiver() {
1922             @Override
1923             public void onReceive(Context context, Intent intent) {
1924                 // The defaultValue of getExtraBoolean should be the opposite of what is
1925                 // expected, thus ensuring a test failure if the extra is absent.
1926                 actualResult.complete(intent.getBooleanExtra("state", !expectedResult));
1927             }
1928         };
1929         try {
1930             mContext.registerReceiver(receiver,
1931                     new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
1932             mCm.setAirplaneMode(expectedResult);
1933             final String msg = "Setting Airplane Mode failed,";
1934             assertEquals(msg, expectedResult, actualResult.get(AIRPLANE_MODE_CHANGE_TIMEOUT_MS,
1935                     TimeUnit.MILLISECONDS));
1936         } finally {
1937             mContext.unregisterReceiver(receiver);
1938         }
1939     }
1940 
1941     private static boolean isAirplaneModeEnabled() {
1942         return runShellCommand("cmd connectivity airplane-mode")
1943                 .trim().equals("enabled");
1944     }
1945 
1946     @Test
1947     public void testGetCaptivePortalServerUrl() {
1948         final String permission = Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q
1949                 ? CONNECTIVITY_INTERNAL
1950                 : NETWORK_SETTINGS;
1951         final String url = runAsShell(permission, mCm::getCaptivePortalServerUrl);
1952         assertNotNull("getCaptivePortalServerUrl must not be null", url);
1953         try {
1954             final URL parsedUrl = new URL(url);
1955             // As per the javadoc, the URL must be HTTP
1956             assertEquals("Invalid captive portal URL protocol", "http", parsedUrl.getProtocol());
1957         } catch (MalformedURLException e) {
1958             throw new AssertionFailedError("Captive portal server URL is invalid: " + e);
1959         }
1960     }
1961 
1962     /**
1963      * Verifies that apps are forbidden from getting ssid information from
1964      * {@Code NetworkCapabilities} if they do not hold NETWORK_SETTINGS permission.
1965      * See b/161370134.
1966      */
1967     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
1968     @Test
1969     public void testSsidInNetworkCapabilities() throws Exception {
1970         assumeTrue("testSsidInNetworkCapabilities cannot execute unless device supports WiFi",
1971                 mPackageManager.hasSystemFeature(FEATURE_WIFI));
1972 
1973         final Network network = mCtsNetUtils.ensureWifiConnected();
1974         final String ssid = unquoteSSID(mWifiManager.getConnectionInfo().getSSID());
1975         assertNotNull("Ssid getting from WifiManager is null", ssid);
1976         // This package should have no NETWORK_SETTINGS permission. Verify that no ssid is contained
1977         // in the NetworkCapabilities.
1978         verifySsidFromQueriedNetworkCapabilities(network, ssid, false /* hasSsid */);
1979         verifySsidFromCallbackNetworkCapabilities(ssid, false /* hasSsid */);
1980         // Adopt shell permission to allow to get ssid information.
1981         runWithShellPermissionIdentity(() -> {
1982             verifySsidFromQueriedNetworkCapabilities(network, ssid, true /* hasSsid */);
1983             verifySsidFromCallbackNetworkCapabilities(ssid, true /* hasSsid */);
1984         });
1985     }
1986 
1987     private void verifySsidFromQueriedNetworkCapabilities(@NonNull Network network,
1988             @NonNull String ssid, boolean hasSsid) throws Exception {
1989         // Verify if ssid is contained in NetworkCapabilities queried from ConnectivityManager.
1990         final NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
1991         assertNotNull("NetworkCapabilities of the network is null", nc);
1992         assertEquals(hasSsid, Pattern.compile(ssid).matcher(nc.toString()).find());
1993     }
1994 
1995     private void verifySsidFromCallbackNetworkCapabilities(@NonNull String ssid, boolean hasSsid)
1996             throws Exception {
1997         final CompletableFuture<NetworkCapabilities> foundNc = new CompletableFuture();
1998         final NetworkCallback callback = new NetworkCallback() {
1999             @Override
2000             public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
2001                 foundNc.complete(nc);
2002             }
2003         };
2004         try {
2005             mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
2006             // Registering a callback here guarantees onCapabilitiesChanged is called immediately
2007             // because WiFi network should be connected.
2008             final NetworkCapabilities nc =
2009                     foundNc.get(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
2010             // Verify if ssid is contained in the NetworkCapabilities received from callback.
2011             assertNotNull("NetworkCapabilities of the network is null", nc);
2012             assertEquals(hasSsid, Pattern.compile(ssid).matcher(nc.toString()).find());
2013         } finally {
2014             mCm.unregisterNetworkCallback(callback);
2015         }
2016     }
2017 
2018     /**
2019      * Verify background request can only be requested when acquiring
2020      * {@link android.Manifest.permission.NETWORK_SETTINGS}.
2021      */
2022     @AppModeFull(reason = "Instant apps cannot create test networks")
2023     @Test
2024     public void testRequestBackgroundNetwork() {
2025         // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
2026         // shims, and @IgnoreUpTo does not check that.
2027         assumeTrue(TestUtils.shouldTestSApis());
2028 
2029         // Create a tun interface. Use the returned interface name as the specifier to create
2030         // a test network request.
2031         final TestNetworkManager tnm = runWithShellPermissionIdentity(() ->
2032                 mContext.getSystemService(TestNetworkManager.class),
2033                 android.Manifest.permission.MANAGE_TEST_NETWORKS);
2034         final TestNetworkInterface testNetworkInterface = runWithShellPermissionIdentity(() ->
2035                     tnm.createTunInterface(new LinkAddress[]{TEST_LINKADDR}),
2036                 android.Manifest.permission.MANAGE_TEST_NETWORKS,
2037                 android.Manifest.permission.NETWORK_SETTINGS);
2038         assertNotNull(testNetworkInterface);
2039 
2040         final NetworkRequest testRequest = new NetworkRequest.Builder()
2041                 .addTransportType(TRANSPORT_TEST)
2042                 // Test networks do not have NOT_VPN or TRUSTED capabilities by default
2043                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
2044                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
2045                 .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
2046                         testNetworkInterface.getInterfaceName()))
2047                 .build();
2048 
2049         // Verify background network cannot be requested without NETWORK_SETTINGS permission.
2050         final TestableNetworkCallback callback = new TestableNetworkCallback();
2051         final Handler handler = new Handler(Looper.getMainLooper());
2052         assertThrows(SecurityException.class,
2053                 () -> mCmShim.requestBackgroundNetwork(testRequest, callback, handler));
2054 
2055         Network testNetwork = null;
2056         try {
2057             // Request background test network via Shell identity which has NETWORK_SETTINGS
2058             // permission granted.
2059             runWithShellPermissionIdentity(
2060                     () -> mCmShim.requestBackgroundNetwork(testRequest, callback, handler),
2061                     new String[] { android.Manifest.permission.NETWORK_SETTINGS });
2062 
2063             // Register the test network agent which has no foreground request associated to it.
2064             // And verify it can satisfy the background network request just fired.
2065             final Binder binder = new Binder();
2066             runWithShellPermissionIdentity(() ->
2067                     tnm.setupTestNetwork(testNetworkInterface.getInterfaceName(), binder),
2068                     new String[] { android.Manifest.permission.MANAGE_TEST_NETWORKS,
2069                             android.Manifest.permission.NETWORK_SETTINGS });
2070             waitForAvailable(callback);
2071             testNetwork = callback.getLastAvailableNetwork();
2072             assertNotNull(testNetwork);
2073 
2074             // The test network that has just connected is a foreground network,
2075             // non-listen requests will get available callback before it can be put into
2076             // background if no foreground request can be satisfied. Thus, wait for a short
2077             // period is needed to let foreground capability go away.
2078             callback.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
2079                     NETWORK_CALLBACK_TIMEOUT_MS,
2080                     c -> c instanceof CallbackEntry.CapabilitiesChanged
2081                             && !((CallbackEntry.CapabilitiesChanged) c).getCaps()
2082                             .hasCapability(NET_CAPABILITY_FOREGROUND));
2083             final NetworkCapabilities nc = mCm.getNetworkCapabilities(testNetwork);
2084             assertFalse("expected background network, but got " + nc,
2085                     nc.hasCapability(NET_CAPABILITY_FOREGROUND));
2086         } finally {
2087             final Network n = testNetwork;
2088             runWithShellPermissionIdentity(() -> {
2089                 if (null != n) {
2090                     tnm.teardownTestNetwork(n);
2091                     callback.eventuallyExpect(CallbackEntry.LOST,
2092                             NETWORK_CALLBACK_TIMEOUT_MS,
2093                             lost -> n.equals(lost.getNetwork()));
2094                 }
2095                 testNetworkInterface.getFileDescriptor().close();
2096             }, new String[] { android.Manifest.permission.MANAGE_TEST_NETWORKS });
2097             mCm.unregisterNetworkCallback(callback);
2098         }
2099     }
2100 
2101     private class DetailedBlockedStatusCallback extends TestableNetworkCallback {
2102         public void expectAvailableCallbacks(Network network) {
2103             super.expectAvailableCallbacks(network, false /* suspended */, true /* validated */,
2104                     BLOCKED_REASON_NONE, NETWORK_CALLBACK_TIMEOUT_MS);
2105         }
2106         public void expectBlockedStatusCallback(Network network, int blockedStatus) {
2107             super.expectBlockedStatusCallback(blockedStatus, network, NETWORK_CALLBACK_TIMEOUT_MS);
2108         }
2109         public void onBlockedStatusChanged(Network network, int blockedReasons) {
2110             getHistory().add(new CallbackEntry.BlockedStatusInt(network, blockedReasons));
2111         }
2112     }
2113 
2114     private void setRequireVpnForUids(boolean requireVpn, Collection<Range<Integer>> ranges)
2115             throws Exception {
2116         mCmShim.setRequireVpnForUids(requireVpn, ranges);
2117         for (Range<Integer> range : ranges) {
2118             if (requireVpn) {
2119                 mVpnRequiredUidRanges.add(range);
2120             } else {
2121                 assertTrue(mVpnRequiredUidRanges.remove(range));
2122             }
2123         }
2124     }
2125 
2126     private void doTestBlockedStatusCallback() throws Exception {
2127         final DetailedBlockedStatusCallback myUidCallback = new DetailedBlockedStatusCallback();
2128         final DetailedBlockedStatusCallback otherUidCallback = new DetailedBlockedStatusCallback();
2129 
2130         final int myUid = Process.myUid();
2131         final int otherUid = UserHandle.getUid(5, Process.FIRST_APPLICATION_UID);
2132         final Handler handler = new Handler(Looper.getMainLooper());
2133         mCm.registerDefaultNetworkCallback(myUidCallback, handler);
2134         mCmShim.registerDefaultNetworkCallbackForUid(otherUid, otherUidCallback, handler);
2135 
2136         final Network defaultNetwork = mCm.getActiveNetwork();
2137         final List<DetailedBlockedStatusCallback> allCallbacks =
2138                 List.of(myUidCallback, otherUidCallback);
2139         for (DetailedBlockedStatusCallback callback : allCallbacks) {
2140             callback.expectAvailableCallbacks(defaultNetwork);
2141         }
2142 
2143         final Range<Integer> myUidRange = new Range<>(myUid, myUid);
2144         final Range<Integer> otherUidRange = new Range<>(otherUid, otherUid);
2145 
2146         setRequireVpnForUids(true, List.of(myUidRange));
2147         myUidCallback.expectBlockedStatusCallback(defaultNetwork, BLOCKED_REASON_LOCKDOWN_VPN);
2148         otherUidCallback.assertNoCallback(NO_CALLBACK_TIMEOUT_MS);
2149 
2150         setRequireVpnForUids(true, List.of(myUidRange, otherUidRange));
2151         myUidCallback.assertNoCallback(NO_CALLBACK_TIMEOUT_MS);
2152         otherUidCallback.expectBlockedStatusCallback(defaultNetwork, BLOCKED_REASON_LOCKDOWN_VPN);
2153 
2154         // setRequireVpnForUids does no deduplication or refcounting. Removing myUidRange does not
2155         // unblock myUid because it was added to the blocked ranges twice.
2156         setRequireVpnForUids(false, List.of(myUidRange));
2157         myUidCallback.assertNoCallback(NO_CALLBACK_TIMEOUT_MS);
2158         otherUidCallback.assertNoCallback(NO_CALLBACK_TIMEOUT_MS);
2159 
2160         setRequireVpnForUids(false, List.of(myUidRange, otherUidRange));
2161         myUidCallback.expectBlockedStatusCallback(defaultNetwork, BLOCKED_REASON_NONE);
2162         otherUidCallback.expectBlockedStatusCallback(defaultNetwork, BLOCKED_REASON_NONE);
2163 
2164         myUidCallback.assertNoCallback(NO_CALLBACK_TIMEOUT_MS);
2165         otherUidCallback.assertNoCallback(NO_CALLBACK_TIMEOUT_MS);
2166     }
2167 
2168     @Test
2169     public void testBlockedStatusCallback() {
2170         // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
2171         // shims, and @IgnoreUpTo does not check that.
2172         assumeTrue(TestUtils.shouldTestSApis());
2173         runWithShellPermissionIdentity(() -> doTestBlockedStatusCallback(), NETWORK_SETTINGS);
2174     }
2175 
2176     private void doTestLegacyLockdownEnabled() throws Exception {
2177         NetworkInfo info = mCm.getActiveNetworkInfo();
2178         assertNotNull(info);
2179         assertEquals(DetailedState.CONNECTED, info.getDetailedState());
2180 
2181         try {
2182             mCmShim.setLegacyLockdownVpnEnabled(true);
2183 
2184             // setLegacyLockdownVpnEnabled is asynchronous and only takes effect when the
2185             // ConnectivityService handler thread processes it. Ensure it has taken effect by doing
2186             // something that blocks until the handler thread is idle.
2187             final TestableNetworkCallback callback = new TestableNetworkCallback();
2188             mCm.registerDefaultNetworkCallback(callback);
2189             waitForAvailable(callback);
2190             mCm.unregisterNetworkCallback(callback);
2191 
2192             // Test one of the effects of setLegacyLockdownVpnEnabled: the fact that any NetworkInfo
2193             // in state CONNECTED is degraded to CONNECTING if the legacy VPN is not connected.
2194             info = mCm.getActiveNetworkInfo();
2195             assertNotNull(info);
2196             assertEquals(DetailedState.CONNECTING, info.getDetailedState());
2197         } finally {
2198             mCmShim.setLegacyLockdownVpnEnabled(false);
2199         }
2200     }
2201 
2202     @Test
2203     public void testLegacyLockdownEnabled() {
2204         // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
2205         // shims, and @IgnoreUpTo does not check that.
2206         assumeTrue(TestUtils.shouldTestSApis());
2207         runWithShellPermissionIdentity(() -> doTestLegacyLockdownEnabled(), NETWORK_SETTINGS);
2208     }
2209 
2210     @Test
2211     public void testGetCapabilityCarrierName() {
2212         assumeTrue(TestUtils.shouldTestSApis());
2213         assertEquals("ENTERPRISE", NetworkInformationShimImpl.newInstance()
2214                 .getCapabilityCarrierName(ConstantsShim.NET_CAPABILITY_ENTERPRISE));
2215         assertNull(NetworkInformationShimImpl.newInstance()
2216                 .getCapabilityCarrierName(ConstantsShim.NET_CAPABILITY_NOT_VCN_MANAGED));
2217     }
2218 
2219     @Test
2220     public void testSetGlobalProxy() {
2221         assumeTrue(TestUtils.shouldTestSApis());
2222         // Behavior is verified in gts. Verify exception thrown w/o permission.
2223         assertThrows(SecurityException.class, () -> mCm.setGlobalProxy(
2224                 ProxyInfo.buildDirectProxy("example.com" /* host */, 8080 /* port */)));
2225     }
2226 
2227     @Test
2228     public void testFactoryResetWithoutPermission() {
2229         assumeTrue(TestUtils.shouldTestSApis());
2230         assertThrows(SecurityException.class, () -> mCm.factoryReset());
2231     }
2232 
2233     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
2234     @Test
2235     public void testFactoryReset() throws Exception {
2236         assumeTrue(TestUtils.shouldTestSApis());
2237 
2238         // Store current settings.
2239         final int curAvoidBadWifi =
2240                 ConnectivitySettingsManager.getNetworkAvoidBadWifi(mContext);
2241         final int curPrivateDnsMode = ConnectivitySettingsManager.getPrivateDnsMode(mContext);
2242 
2243         TestTetheringEventCallback tetherEventCallback = null;
2244         final CtsTetheringUtils tetherUtils = new CtsTetheringUtils(mContext);
2245         try {
2246             tetherEventCallback = tetherUtils.registerTetheringEventCallback();
2247             // Adopt for NETWORK_SETTINGS permission.
2248             mUiAutomation.adoptShellPermissionIdentity();
2249             // start tethering
2250             tetherEventCallback.assumeWifiTetheringSupported(mContext);
2251             tetherUtils.startWifiTethering(tetherEventCallback);
2252             // Update setting to verify the behavior.
2253             mCm.setAirplaneMode(true);
2254             ConnectivitySettingsManager.setPrivateDnsMode(mContext,
2255                     ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF);
2256             ConnectivitySettingsManager.setNetworkAvoidBadWifi(mContext,
2257                     ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI_IGNORE);
2258             assertEquals(AIRPLANE_MODE_ON, Settings.Global.getInt(
2259                     mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON));
2260             // Verify factoryReset
2261             mCm.factoryReset();
2262             verifySettings(AIRPLANE_MODE_OFF,
2263                     ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC,
2264                     ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI_PROMPT);
2265 
2266             tetherEventCallback.expectNoTetheringActive();
2267         } finally {
2268             // Restore settings.
2269             mCm.setAirplaneMode(false);
2270             ConnectivitySettingsManager.setNetworkAvoidBadWifi(mContext, curAvoidBadWifi);
2271             ConnectivitySettingsManager.setPrivateDnsMode(mContext, curPrivateDnsMode);
2272             if (tetherEventCallback != null) {
2273                 tetherUtils.unregisterTetheringEventCallback(tetherEventCallback);
2274             }
2275             tetherUtils.stopAllTethering();
2276             mUiAutomation.dropShellPermissionIdentity();
2277         }
2278     }
2279 
2280     /**
2281      * Verify that {@link ConnectivityManager#setProfileNetworkPreference} cannot be called
2282      * without required NETWORK_STACK permissions.
2283      */
2284     @Test
2285     public void testSetProfileNetworkPreference_NoPermission() {
2286         // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
2287         // shims, and @IgnoreUpTo does not check that.
2288         assumeTrue(TestUtils.shouldTestSApis());
2289         assertThrows(SecurityException.class, () -> mCm.setProfileNetworkPreference(
2290                 UserHandle.of(0), PROFILE_NETWORK_PREFERENCE_ENTERPRISE, null /* executor */,
2291                 null /* listener */));
2292     }
2293 
2294     @Test
2295     public void testSystemReady() {
2296         assumeTrue(TestUtils.shouldTestSApis());
2297         assertThrows(SecurityException.class, () -> mCm.systemReady());
2298     }
2299 
2300     @Test
2301     public void testGetIpSecNetIdRange() {
2302         assumeTrue(TestUtils.shouldTestSApis());
2303         // The lower refers to ConnectivityManager.TUN_INTF_NETID_START.
2304         final long lower = 64512;
2305         // The upper refers to ConnectivityManager.TUN_INTF_NETID_START
2306         // + ConnectivityManager.TUN_INTF_NETID_RANGE - 1
2307         final long upper = 65535;
2308         assertEquals(lower, (long) ConnectivityManager.getIpSecNetIdRange().getLower());
2309         assertEquals(upper, (long) ConnectivityManager.getIpSecNetIdRange().getUpper());
2310     }
2311 
2312     private void verifySettings(int expectedAirplaneMode, int expectedPrivateDnsMode,
2313             int expectedAvoidBadWifi) throws Exception {
2314         assertEquals(expectedAirplaneMode, Settings.Global.getInt(
2315                 mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON));
2316         assertEquals(expectedPrivateDnsMode,
2317                 ConnectivitySettingsManager.getPrivateDnsMode(mContext));
2318         assertEquals(expectedAvoidBadWifi,
2319                 ConnectivitySettingsManager.getNetworkAvoidBadWifi(mContext));
2320     }
2321 
2322     /**
2323      * Verify that per-app OEM network preference functions as expected for network preference TEST.
2324      * For specified apps, validate networks are prioritized in order: unmetered, TEST transport,
2325      * default network.
2326      */
2327     @AppModeFull(reason = "Instant apps cannot create test networks")
2328     @Test
2329     public void testSetOemNetworkPreferenceForTestPref() throws Exception {
2330         // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
2331         // shims, and @IgnoreUpTo does not check that.
2332         assumeTrue(TestUtils.shouldTestSApis());
2333         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
2334 
2335         final TestNetworkTracker tnt = callWithShellPermissionIdentity(
2336                 () -> initTestNetwork(mContext, TEST_LINKADDR, NETWORK_CALLBACK_TIMEOUT_MS));
2337         final TestableNetworkCallback defaultCallback = new TestableNetworkCallback();
2338         final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback();
2339 
2340         final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
2341         final NetworkCapabilities wifiNetworkCapabilities = callWithShellPermissionIdentity(
2342                 () -> mCm.getNetworkCapabilities(wifiNetwork));
2343         final String ssid = unquoteSSID(wifiNetworkCapabilities.getSsid());
2344         final boolean oldMeteredValue = wifiNetworkCapabilities.isMetered();
2345 
2346         try {
2347             // This network will be used for unmetered. Wait for it to be validated because
2348             // OEM_NETWORK_PREFERENCE_TEST only prefers NOT_METERED&VALIDATED to a network with
2349             // TRANSPORT_TEST, like OEM_NETWORK_PREFERENCE_OEM_PAID.
2350             setWifiMeteredStatusAndWait(ssid, false /* isMetered */, true /* waitForValidation */);
2351 
2352             setOemNetworkPreferenceForMyPackage(OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST);
2353             registerTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
2354 
2355             // Validate that an unmetered network is used over other networks.
2356             waitForAvailable(defaultCallback, wifiNetwork);
2357             waitForAvailable(systemDefaultCallback, wifiNetwork);
2358 
2359             // Validate that when setting unmetered to metered, unmetered is lost and replaced by
2360             // the network with the TEST transport. Also wait for validation here, in case there
2361             // is a bug that's only visible when the network is validated.
2362             setWifiMeteredStatusAndWait(ssid, true /* isMetered */, true /* waitForValidation */);
2363             defaultCallback.expectCallback(CallbackEntry.LOST, wifiNetwork,
2364                     NETWORK_CALLBACK_TIMEOUT_MS);
2365             waitForAvailable(defaultCallback, tnt.getNetwork());
2366             // Depending on if this device has cellular connectivity or not, multiple available
2367             // callbacks may be received. Eventually, metered Wi-Fi should be the final available
2368             // callback in any case therefore confirm its receipt before continuing to assure the
2369             // system is in the expected state.
2370             waitForAvailable(systemDefaultCallback, TRANSPORT_WIFI);
2371         } finally {
2372             // Validate that removing the test network will fallback to the default network.
2373             runWithShellPermissionIdentity(tnt::teardown);
2374             defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork(),
2375                     NETWORK_CALLBACK_TIMEOUT_MS);
2376             waitForAvailable(defaultCallback);
2377 
2378             setWifiMeteredStatusAndWait(ssid, oldMeteredValue, false /* waitForValidation */);
2379 
2380             // Cleanup any prior test state from setOemNetworkPreference
2381             clearOemNetworkPreference();
2382             unregisterTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
2383         }
2384     }
2385 
2386     /**
2387      * Verify that per-app OEM network preference functions as expected for network pref TEST_ONLY.
2388      * For specified apps, validate that only TEST transport type networks are used.
2389      */
2390     @AppModeFull(reason = "Instant apps cannot create test networks")
2391     @Test
2392     public void testSetOemNetworkPreferenceForTestOnlyPref() throws Exception {
2393         // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
2394         // shims, and @IgnoreUpTo does not check that.
2395         assumeTrue(TestUtils.shouldTestSApis());
2396 
2397         final TestNetworkTracker tnt = callWithShellPermissionIdentity(
2398                 () -> initTestNetwork(mContext, TEST_LINKADDR, NETWORK_CALLBACK_TIMEOUT_MS));
2399         final TestableNetworkCallback defaultCallback = new TestableNetworkCallback();
2400         final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback();
2401 
2402         final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
2403 
2404         try {
2405             setOemNetworkPreferenceForMyPackage(
2406                     OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY);
2407             registerTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
2408             waitForAvailable(defaultCallback, tnt.getNetwork());
2409             waitForAvailable(systemDefaultCallback, wifiNetwork);
2410         } finally {
2411             runWithShellPermissionIdentity(tnt::teardown);
2412             defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork(),
2413                     NETWORK_CALLBACK_TIMEOUT_MS);
2414 
2415             // This network preference should only ever use the test network therefore available
2416             // should not trigger when the test network goes down (e.g. switch to cellular).
2417             defaultCallback.assertNoCallback();
2418             // The system default should still be connected to Wi-fi
2419             assertEquals(wifiNetwork, systemDefaultCallback.getLastAvailableNetwork());
2420 
2421             // Cleanup any prior test state from setOemNetworkPreference
2422             clearOemNetworkPreference();
2423 
2424             // The default (non-test) network should be available as the network pref was cleared.
2425             waitForAvailable(defaultCallback);
2426             unregisterTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
2427         }
2428     }
2429 
2430     private void unregisterTestOemNetworkPreferenceCallbacks(
2431             @NonNull final TestableNetworkCallback defaultCallback,
2432             @NonNull final TestableNetworkCallback systemDefaultCallback) {
2433         mCm.unregisterNetworkCallback(defaultCallback);
2434         mCm.unregisterNetworkCallback(systemDefaultCallback);
2435     }
2436 
2437     private void registerTestOemNetworkPreferenceCallbacks(
2438             @NonNull final TestableNetworkCallback defaultCallback,
2439             @NonNull final TestableNetworkCallback systemDefaultCallback) {
2440         mCm.registerDefaultNetworkCallback(defaultCallback);
2441         runWithShellPermissionIdentity(() ->
2442                 mCmShim.registerSystemDefaultNetworkCallback(systemDefaultCallback,
2443                         new Handler(Looper.getMainLooper())), NETWORK_SETTINGS);
2444     }
2445 
2446     private static final class OnCompleteListenerCallback {
2447         final CompletableFuture<Object> mDone = new CompletableFuture<>();
2448 
2449         public void onComplete() {
2450             mDone.complete(new Object());
2451         }
2452 
2453         void expectOnComplete() throws Exception {
2454             try {
2455                 mDone.get(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
2456             } catch (TimeoutException e) {
2457                 fail("Expected onComplete() not received after "
2458                         + NETWORK_CALLBACK_TIMEOUT_MS + " ms");
2459             }
2460         }
2461     }
2462 
2463     private void setOemNetworkPreferenceForMyPackage(final int networkPref) throws Exception {
2464         final OemNetworkPreferences pref = new OemNetworkPreferences.Builder()
2465                 .addNetworkPreference(mContext.getPackageName(), networkPref)
2466                 .build();
2467         final OnCompleteListenerCallback oemPrefListener = new OnCompleteListenerCallback();
2468         mUiAutomation.adoptShellPermissionIdentity();
2469         try {
2470             mCm.setOemNetworkPreference(
2471                     pref, mContext.getMainExecutor(), oemPrefListener::onComplete);
2472         } finally {
2473             mUiAutomation.dropShellPermissionIdentity();
2474         }
2475         oemPrefListener.expectOnComplete();
2476     }
2477 
2478     /**
2479      * This will clear the OEM network preference on the device. As there is currently no way of
2480      * getting the existing preference, if this is executed while an existing preference is in
2481      * place, that preference will need to be reapplied after executing this test.
2482      * @throws Exception
2483      */
2484     private void clearOemNetworkPreference() throws Exception {
2485         final OemNetworkPreferences clearPref = new OemNetworkPreferences.Builder().build();
2486         final OnCompleteListenerCallback oemPrefListener = new OnCompleteListenerCallback();
2487         mUiAutomation.adoptShellPermissionIdentity();
2488         try {
2489             mCm.setOemNetworkPreference(
2490                     clearPref, mContext.getMainExecutor(), oemPrefListener::onComplete);
2491         } finally {
2492             mUiAutomation.dropShellPermissionIdentity();
2493         }
2494         oemPrefListener.expectOnComplete();
2495     }
2496 
2497     @Test
2498     public void testSetAcceptPartialConnectivity_NoPermission_GetException() {
2499         assumeTrue(TestUtils.shouldTestSApis());
2500         assertThrows(SecurityException.class, () -> mCm.setAcceptPartialConnectivity(
2501                 mCm.getActiveNetwork(), false /* accept */ , false /* always */));
2502     }
2503 
2504     @AppModeFull(reason = "WRITE_DEVICE_CONFIG permission can't be granted to instant apps")
2505     @Test
2506     public void testAcceptPartialConnectivity_validatedNetwork() throws Exception {
2507         assumeTrue(TestUtils.shouldTestSApis());
2508         assumeTrue("testAcceptPartialConnectivity_validatedNetwork cannot execute"
2509                         + " unless device supports WiFi",
2510                 mPackageManager.hasSystemFeature(FEATURE_WIFI));
2511 
2512         try {
2513             // Wait for partial connectivity to be detected on the network
2514             final Network network = preparePartialConnectivity();
2515 
2516             runAsShell(NETWORK_SETTINGS, () -> {
2517                 // The always bit is verified in NetworkAgentTest
2518                 mCm.setAcceptPartialConnectivity(network, true /* accept */, false /* always */);
2519             });
2520 
2521             // Accept partial connectivity network should result in a validated network
2522             expectNetworkHasCapability(network, NET_CAPABILITY_VALIDATED, WIFI_CONNECT_TIMEOUT_MS);
2523         } finally {
2524             resetValidationConfig();
2525             // Reconnect wifi to reset the wifi status
2526             reconnectWifi();
2527         }
2528     }
2529 
2530     @AppModeFull(reason = "WRITE_DEVICE_CONFIG permission can't be granted to instant apps")
2531     @Test
2532     public void testRejectPartialConnectivity_TearDownNetwork() throws Exception {
2533         assumeTrue(TestUtils.shouldTestSApis());
2534         assumeTrue("testAcceptPartialConnectivity_validatedNetwork cannot execute"
2535                         + " unless device supports WiFi",
2536                 mPackageManager.hasSystemFeature(FEATURE_WIFI));
2537 
2538         final TestNetworkCallback cb = new TestNetworkCallback();
2539         try {
2540             // Wait for partial connectivity to be detected on the network
2541             final Network network = preparePartialConnectivity();
2542 
2543             mCm.requestNetwork(makeWifiNetworkRequest(), cb);
2544             runAsShell(NETWORK_SETTINGS, () -> {
2545                 // The always bit is verified in NetworkAgentTest
2546                 mCm.setAcceptPartialConnectivity(network, false /* accept */, false /* always */);
2547             });
2548             // Reject partial connectivity network should cause the network being torn down
2549             assertEquals(network, cb.waitForLost());
2550         } finally {
2551             mCm.unregisterNetworkCallback(cb);
2552             resetValidationConfig();
2553             // Wifi will not automatically reconnect to the network. ensureWifiDisconnected cannot
2554             // apply here. Thus, turn off wifi first and restart to restore.
2555             runShellCommand("svc wifi disable");
2556             mCtsNetUtils.ensureWifiConnected();
2557         }
2558     }
2559 
2560     @Test
2561     public void testSetAcceptUnvalidated_NoPermission_GetException() {
2562         assumeTrue(TestUtils.shouldTestSApis());
2563         assertThrows(SecurityException.class, () -> mCm.setAcceptUnvalidated(
2564                 mCm.getActiveNetwork(), false /* accept */ , false /* always */));
2565     }
2566 
2567     @AppModeFull(reason = "WRITE_DEVICE_CONFIG permission can't be granted to instant apps")
2568     @Test
2569     public void testRejectUnvalidated_TearDownNetwork() throws Exception {
2570         assumeTrue(TestUtils.shouldTestSApis());
2571         final boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
2572                 && mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
2573         assumeTrue("testAcceptPartialConnectivity_validatedNetwork cannot execute"
2574                         + " unless device supports WiFi and telephony", canRunTest);
2575 
2576         final TestableNetworkCallback wifiCb = new TestableNetworkCallback();
2577         try {
2578             // Ensure at least one default network candidate connected.
2579             mCtsNetUtils.connectToCell();
2580 
2581             final Network wifiNetwork = prepareUnvalidatedNetwork();
2582             // Default network should not be wifi ,but checking that wifi is not the default doesn't
2583             // guarantee that it won't become the default in the future.
2584             assertNotEquals(wifiNetwork, mCm.getActiveNetwork());
2585 
2586             mCm.registerNetworkCallback(makeWifiNetworkRequest(), wifiCb);
2587             runAsShell(NETWORK_SETTINGS, () -> {
2588                 mCm.setAcceptUnvalidated(wifiNetwork, false /* accept */, false /* always */);
2589             });
2590             waitForLost(wifiCb);
2591         } finally {
2592             mCm.unregisterNetworkCallback(wifiCb);
2593             resetValidationConfig();
2594             /// Wifi will not automatically reconnect to the network. ensureWifiDisconnected cannot
2595             // apply here. Thus, turn off wifi first and restart to restore.
2596             runShellCommand("svc wifi disable");
2597             mCtsNetUtils.ensureWifiConnected();
2598         }
2599     }
2600 
2601     @AppModeFull(reason = "WRITE_DEVICE_CONFIG permission can't be granted to instant apps")
2602     @Test
2603     public void testSetAvoidUnvalidated() throws Exception {
2604         assumeTrue(TestUtils.shouldTestSApis());
2605         // TODO: Allow in debuggable ROM only. To be replaced by FabricatedOverlay
2606         assumeTrue(Build.isDebuggable());
2607         final boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
2608                 && mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
2609         assumeTrue("testSetAvoidUnvalidated cannot execute"
2610                 + " unless device supports WiFi and telephony", canRunTest);
2611 
2612         final TestableNetworkCallback wifiCb = new TestableNetworkCallback();
2613         final TestableNetworkCallback defaultCb = new TestableNetworkCallback();
2614         final int previousAvoidBadWifi =
2615                 ConnectivitySettingsManager.getNetworkAvoidBadWifi(mContext);
2616 
2617         allowBadWifi();
2618 
2619         final Network cellNetwork = mCtsNetUtils.connectToCell();
2620         final Network wifiNetwork = prepareValidatedNetwork();
2621 
2622         mCm.registerDefaultNetworkCallback(defaultCb);
2623         mCm.registerNetworkCallback(makeWifiNetworkRequest(), wifiCb);
2624 
2625         try {
2626             // Verify wifi is the default network.
2627             defaultCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
2628                     entry -> wifiNetwork.equals(entry.getNetwork()));
2629             wifiCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
2630                     entry -> wifiNetwork.equals(entry.getNetwork()));
2631             assertTrue(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
2632                     NET_CAPABILITY_VALIDATED));
2633 
2634             // Configure response code for unvalidated network
2635             configTestServer(Status.INTERNAL_ERROR, Status.INTERNAL_ERROR);
2636             mCm.reportNetworkConnectivity(wifiNetwork, false);
2637             // Default network should stay on unvalidated wifi because avoid bad wifi is disabled.
2638             defaultCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
2639                     NETWORK_CALLBACK_TIMEOUT_MS,
2640                     entry -> !((CallbackEntry.CapabilitiesChanged) entry).getCaps()
2641                             .hasCapability(NET_CAPABILITY_VALIDATED));
2642             wifiCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
2643                     NETWORK_CALLBACK_TIMEOUT_MS,
2644                     entry -> !((CallbackEntry.CapabilitiesChanged) entry).getCaps()
2645                             .hasCapability(NET_CAPABILITY_VALIDATED));
2646 
2647             runAsShell(NETWORK_SETTINGS, () -> {
2648                 mCm.setAvoidUnvalidated(wifiNetwork);
2649             });
2650             // Default network should be updated to validated cellular network.
2651             defaultCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
2652                     entry -> cellNetwork.equals(entry.getNetwork()));
2653             // No update on wifi callback.
2654             wifiCb.assertNoCallback();
2655         } finally {
2656             mCm.unregisterNetworkCallback(wifiCb);
2657             mCm.unregisterNetworkCallback(defaultCb);
2658             resetAvoidBadWifi(previousAvoidBadWifi);
2659             resetValidationConfig();
2660             // Reconnect wifi to reset the wifi status
2661             reconnectWifi();
2662         }
2663     }
2664 
2665     private void resetAvoidBadWifi(int settingValue) {
2666         setTestAllowBadWifiResource(0 /* timeMs */);
2667         ConnectivitySettingsManager.setNetworkAvoidBadWifi(mContext, settingValue);
2668     }
2669 
2670     private void allowBadWifi() {
2671         setTestAllowBadWifiResource(
2672                 System.currentTimeMillis() + WIFI_CONNECT_TIMEOUT_MS /* timeMs */);
2673         ConnectivitySettingsManager.setNetworkAvoidBadWifi(mContext,
2674                 ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI_IGNORE);
2675     }
2676 
2677     private void setTestAllowBadWifiResource(long timeMs) {
2678         runAsShell(NETWORK_SETTINGS, () -> {
2679             mCm.setTestAllowBadWifiUntil(timeMs);
2680         });
2681     }
2682 
2683     private Network expectNetworkHasCapability(Network network, int expectedNetCap, long timeout)
2684             throws Exception {
2685         final CompletableFuture<Network> future = new CompletableFuture();
2686         final NetworkCallback cb = new NetworkCallback() {
2687             @Override
2688             public void onCapabilitiesChanged(Network n, NetworkCapabilities nc) {
2689                 if (n.equals(network) && nc.hasCapability(expectedNetCap)) {
2690                     future.complete(network);
2691                 }
2692             }
2693         };
2694 
2695         try {
2696             mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), cb);
2697             return future.get(timeout, TimeUnit.MILLISECONDS);
2698         } finally {
2699             mCm.unregisterNetworkCallback(cb);
2700         }
2701     }
2702 
2703     private void resetValidationConfig() {
2704         NetworkValidationTestUtil.clearValidationTestUrlsDeviceConfig();
2705         mHttpServer.stop();
2706     }
2707 
2708     private void prepareHttpServer() throws Exception {
2709         runAsShell(READ_DEVICE_CONFIG, () -> {
2710             // Verify that the test URLs are not normally set on the device, but do not fail if the
2711             // test URLs are set to what this test uses (URLs on localhost), in case the test was
2712             // interrupted manually and rerun.
2713             assertEmptyOrLocalhostUrl(TEST_CAPTIVE_PORTAL_HTTPS_URL);
2714             assertEmptyOrLocalhostUrl(TEST_CAPTIVE_PORTAL_HTTP_URL);
2715         });
2716 
2717         NetworkValidationTestUtil.clearValidationTestUrlsDeviceConfig();
2718 
2719         mHttpServer.start();
2720     }
2721 
2722     private Network reconnectWifi() {
2723         mCtsNetUtils.ensureWifiDisconnected(null /* wifiNetworkToCheck */);
2724         return mCtsNetUtils.ensureWifiConnected();
2725     }
2726 
2727     private Network prepareValidatedNetwork() throws Exception {
2728         prepareHttpServer();
2729         configTestServer(Status.NO_CONTENT, Status.NO_CONTENT);
2730         // Disconnect wifi first then start wifi network with configuration.
2731         final Network wifiNetwork = reconnectWifi();
2732 
2733         return expectNetworkHasCapability(wifiNetwork, NET_CAPABILITY_VALIDATED,
2734                 WIFI_CONNECT_TIMEOUT_MS);
2735     }
2736 
2737     private Network preparePartialConnectivity() throws Exception {
2738         prepareHttpServer();
2739         // Configure response code for partial connectivity
2740         configTestServer(Status.INTERNAL_ERROR  /* httpsStatusCode */,
2741                 Status.NO_CONTENT  /* httpStatusCode */);
2742         // Disconnect wifi first then start wifi network with configuration.
2743         mCtsNetUtils.ensureWifiDisconnected(null /* wifiNetworkToCheck */);
2744         final Network network = mCtsNetUtils.ensureWifiConnected();
2745 
2746         return expectNetworkHasCapability(network, NET_CAPABILITY_PARTIAL_CONNECTIVITY,
2747                 WIFI_CONNECT_TIMEOUT_MS);
2748     }
2749 
2750     private Network prepareUnvalidatedNetwork() throws Exception {
2751         prepareHttpServer();
2752         // Configure response code for unvalidated network
2753         configTestServer(Status.INTERNAL_ERROR /* httpsStatusCode */,
2754                 Status.INTERNAL_ERROR /* httpStatusCode */);
2755 
2756         // Disconnect wifi first then start wifi network with configuration.
2757         mCtsNetUtils.ensureWifiDisconnected(null /* wifiNetworkToCheck */);
2758         final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
2759         return expectNetworkHasCapability(wifiNetwork, NET_CAPABILITY_INTERNET,
2760                 WIFI_CONNECT_TIMEOUT_MS);
2761     }
2762 
2763     private String makeUrl(String path) {
2764         return "http://localhost:" + mHttpServer.getListeningPort() + path;
2765     }
2766 
2767     private void assertEmptyOrLocalhostUrl(String urlKey) {
2768         final String url = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, urlKey);
2769         assertTrue(urlKey + " must not be set in production scenarios, current value= " + url,
2770                 TextUtils.isEmpty(url) || LOCALHOST_HOSTNAME.equals(Uri.parse(url).getHost()));
2771     }
2772 
2773     private void configTestServer(IStatus httpsStatusCode, IStatus httpStatusCode) {
2774         mHttpServer.addResponse(new TestHttpServer.Request(
2775                 TEST_HTTPS_URL_PATH, Method.GET, "" /* queryParameters */),
2776                 httpsStatusCode, null /* locationHeader */, "" /* content */);
2777         mHttpServer.addResponse(new TestHttpServer.Request(
2778                 TEST_HTTP_URL_PATH, Method.GET, "" /* queryParameters */),
2779                 httpStatusCode, null /* locationHeader */, "" /* content */);
2780         NetworkValidationTestUtil.setHttpsUrlDeviceConfig(makeUrl(TEST_HTTPS_URL_PATH));
2781         NetworkValidationTestUtil.setHttpUrlDeviceConfig(makeUrl(TEST_HTTP_URL_PATH));
2782         NetworkValidationTestUtil.setUrlExpirationDeviceConfig(
2783                 System.currentTimeMillis() + WIFI_CONNECT_TIMEOUT_MS);
2784     }
2785 
2786     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
2787     @Test
2788     public void testMobileDataPreferredUids() throws Exception {
2789         assumeTrue(TestUtils.shouldTestSApis());
2790         final boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
2791                 && mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
2792         assumeTrue("testMobileDataPreferredUidsWithCallback cannot execute"
2793                 + " unless device supports both WiFi and telephony", canRunTest);
2794 
2795         final int uid = mPackageManager.getPackageUid(mContext.getPackageName(), 0 /* flag */);
2796         final Set<Integer> mobileDataPreferredUids =
2797                 ConnectivitySettingsManager.getMobileDataPreferredUids(mContext);
2798         // CtsNetTestCases uid should not list in MOBILE_DATA_PREFERRED_UIDS setting because it just
2799         // installs to device. In case the uid is existed in setting mistakenly, try to remove the
2800         // uid and set correct uids to setting.
2801         mobileDataPreferredUids.remove(uid);
2802         ConnectivitySettingsManager.setMobileDataPreferredUids(mContext, mobileDataPreferredUids);
2803 
2804         // For testing mobile data preferred uids feature, it needs both wifi and cell network.
2805         final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
2806         final Network cellNetwork = mCtsNetUtils.connectToCell();
2807         final TestableNetworkCallback defaultTrackingCb = new TestableNetworkCallback();
2808         final TestableNetworkCallback systemDefaultCb = new TestableNetworkCallback();
2809         final Handler h = new Handler(Looper.getMainLooper());
2810         runWithShellPermissionIdentity(() -> mCm.registerSystemDefaultNetworkCallback(
2811                 systemDefaultCb, h), NETWORK_SETTINGS);
2812         mCm.registerDefaultNetworkCallback(defaultTrackingCb);
2813 
2814         try {
2815             // CtsNetTestCases uid is not listed in MOBILE_DATA_PREFERRED_UIDS setting, so the
2816             // per-app default network should be same as system default network.
2817             waitForAvailable(systemDefaultCb, wifiNetwork);
2818             defaultTrackingCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
2819                     entry -> wifiNetwork.equals(entry.getNetwork()));
2820             // Active network for CtsNetTestCases uid should be wifi now.
2821             assertEquals(wifiNetwork, mCm.getActiveNetwork());
2822 
2823             // Add CtsNetTestCases uid to MOBILE_DATA_PREFERRED_UIDS setting, then available per-app
2824             // default network callback should be received with cell network.
2825             final Set<Integer> newMobileDataPreferredUids = new ArraySet<>(mobileDataPreferredUids);
2826             newMobileDataPreferredUids.add(uid);
2827             ConnectivitySettingsManager.setMobileDataPreferredUids(
2828                     mContext, newMobileDataPreferredUids);
2829             defaultTrackingCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
2830                     entry -> cellNetwork.equals(entry.getNetwork()));
2831             // System default network doesn't change.
2832             systemDefaultCb.assertNoCallback();
2833             // Active network for CtsNetTestCases uid should change to cell, too.
2834             assertEquals(cellNetwork, mCm.getActiveNetwork());
2835 
2836             // Remove CtsNetTestCases uid from MOBILE_DATA_PREFERRED_UIDS setting, then available
2837             // per-app default network callback should be received again with system default network
2838             newMobileDataPreferredUids.remove(uid);
2839             ConnectivitySettingsManager.setMobileDataPreferredUids(
2840                     mContext, newMobileDataPreferredUids);
2841             defaultTrackingCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
2842                     entry -> wifiNetwork.equals(entry.getNetwork()));
2843             // System default network still doesn't change.
2844             systemDefaultCb.assertNoCallback();
2845             // Active network for CtsNetTestCases uid should change back to wifi.
2846             assertEquals(wifiNetwork, mCm.getActiveNetwork());
2847         } finally {
2848             mCm.unregisterNetworkCallback(systemDefaultCb);
2849             mCm.unregisterNetworkCallback(defaultTrackingCb);
2850 
2851             // Restore setting.
2852             ConnectivitySettingsManager.setMobileDataPreferredUids(
2853                     mContext, mobileDataPreferredUids);
2854         }
2855     }
2856 
2857     /** Wait for assigned time. */
2858     private void waitForMs(long ms) {
2859         try {
2860             Thread.sleep(ms);
2861         } catch (InterruptedException e) {
2862             fail("Thread was interrupted");
2863         }
2864     }
2865 
2866     private void assertBindSocketToNetworkSuccess(final Network network) throws Exception {
2867         final CompletableFuture<Boolean> future = new CompletableFuture<>();
2868         final ExecutorService executor = Executors.newSingleThreadExecutor();
2869         try {
2870             executor.execute(() -> {
2871                 for (int i = 0; i < 30; i++) {
2872                     waitForMs(100);
2873 
2874                     try (Socket socket = new Socket()) {
2875                         network.bindSocket(socket);
2876                         future.complete(true);
2877                         return;
2878                     } catch (IOException e) { }
2879                 }
2880             });
2881             assertTrue(future.get(APPLYING_UIDS_ALLOWED_ON_RESTRICTED_NETWORKS_TIMEOUT_MS,
2882                     TimeUnit.MILLISECONDS));
2883         } finally {
2884             executor.shutdown();
2885         }
2886     }
2887 
2888     @AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
2889     @Test
2890     public void testUidsAllowedOnRestrictedNetworks() throws Exception {
2891         assumeTrue(TestUtils.shouldTestSApis());
2892 
2893         // TODO (b/175199465): figure out a reasonable permission check for
2894         //  setUidsAllowedOnRestrictedNetworks that allows tests but not system-external callers.
2895         assumeTrue(Build.isDebuggable());
2896 
2897         final int uid = mPackageManager.getPackageUid(mContext.getPackageName(), 0 /* flag */);
2898         final Set<Integer> originalUidsAllowedOnRestrictedNetworks =
2899                 ConnectivitySettingsManager.getUidsAllowedOnRestrictedNetworks(mContext);
2900         // CtsNetTestCases uid should not list in UIDS_ALLOWED_ON_RESTRICTED_NETWORKS setting
2901         // because it has been just installed to device. In case the uid is existed in setting
2902         // mistakenly, try to remove the uid and set correct uids to setting.
2903         originalUidsAllowedOnRestrictedNetworks.remove(uid);
2904         runWithShellPermissionIdentity(() ->
2905                 ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
2906                         mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
2907 
2908         final Handler h = new Handler(Looper.getMainLooper());
2909         final TestableNetworkCallback testNetworkCb = new TestableNetworkCallback();
2910         mCm.registerBestMatchingNetworkCallback(new NetworkRequest.Builder().clearCapabilities()
2911                 .addTransportType(NetworkCapabilities.TRANSPORT_TEST).build(), testNetworkCb, h);
2912 
2913         // Create test network agent with restricted network.
2914         final NetworkCapabilities nc = new NetworkCapabilities.Builder()
2915                 .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
2916                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
2917                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
2918                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
2919                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
2920                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
2921                 .build();
2922         final NetworkScore score = new NetworkScore.Builder()
2923                 .setExiting(false)
2924                 .setTransportPrimary(false)
2925                 .setKeepConnectedReason(NetworkScore.KEEP_CONNECTED_FOR_HANDOVER)
2926                 .build();
2927         final NetworkAgent agent = new NetworkAgent(mContext, Looper.getMainLooper(),
2928                 TAG, nc, new LinkProperties(), score, new NetworkAgentConfig.Builder().build(),
2929                 new NetworkProvider(mContext, Looper.getMainLooper(), TAG)) {};
2930         runWithShellPermissionIdentity(() -> agent.register(),
2931                 android.Manifest.permission.MANAGE_TEST_NETWORKS);
2932         agent.markConnected();
2933 
2934         final Network network = agent.getNetwork();
2935 
2936         try (Socket socket = new Socket()) {
2937             testNetworkCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
2938                     entry -> network.equals(entry.getNetwork()));
2939             // Verify that the network is restricted.
2940             final NetworkCapabilities testNetworkNc = mCm.getNetworkCapabilities(network);
2941             assertNotNull(testNetworkNc);
2942             assertFalse(testNetworkNc.hasCapability(
2943                     NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED));
2944             // CtsNetTestCases package doesn't hold CONNECTIVITY_USE_RESTRICTED_NETWORKS, so it
2945             // does not allow to bind socket to restricted network.
2946             assertThrows(IOException.class, () -> network.bindSocket(socket));
2947 
2948             // Add CtsNetTestCases uid to UIDS_ALLOWED_ON_RESTRICTED_NETWORKS setting, then it can
2949             // bind socket to restricted network normally.
2950             final Set<Integer> newUidsAllowedOnRestrictedNetworks =
2951                     new ArraySet<>(originalUidsAllowedOnRestrictedNetworks);
2952             newUidsAllowedOnRestrictedNetworks.add(uid);
2953             runWithShellPermissionIdentity(() ->
2954                     ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
2955                             mContext, newUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
2956             // Wait a while for sending allowed uids on the restricted network to netd.
2957             // TODD: Have a significant signal to know the uids has been send to netd.
2958             assertBindSocketToNetworkSuccess(network);
2959         } finally {
2960             mCm.unregisterNetworkCallback(testNetworkCb);
2961             agent.unregister();
2962 
2963             // Restore setting.
2964             runWithShellPermissionIdentity(() ->
2965                     ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
2966                             mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
2967         }
2968     }
2969 }
2970