Merge "Skip NetworkStatsBinderTest on Q" into rvc-dev
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index 46fae33..93a6d91 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -77,6 +77,7 @@
 android_test {
     name: "CtsNetTestCasesLatestSdk",
     defaults: ["CtsNetTestCasesDefaults"],
+    jni_uses_sdk_apis: true,
     min_sdk_version: "29",
     target_sdk_version: "29",
     test_suites: [
diff --git a/tests/cts/net/AndroidManifest.xml b/tests/cts/net/AndroidManifest.xml
index baf914f..a7e2bd7 100644
--- a/tests/cts/net/AndroidManifest.xml
+++ b/tests/cts/net/AndroidManifest.xml
@@ -26,6 +26,7 @@
     <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/cts/net/ipsec/AndroidTest.xml b/tests/cts/net/ipsec/AndroidTest.xml
index 09e5c93..cd5c118 100644
--- a/tests/cts/net/ipsec/AndroidTest.xml
+++ b/tests/cts/net/ipsec/AndroidTest.xml
@@ -27,4 +27,7 @@
         <option name="package" value="android.net.ipsec.cts" />
         <option name="hidden-api-checks" value="false" />
     </test>
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+        <option name="mainline-module-package-name" value="com.google.android.ipsec" />
+    </object>
 </configuration>
diff --git a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java
index ed67dd1..fb93398 100644
--- a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java
+++ b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java
@@ -99,19 +99,17 @@
                     .addInternalAddressRequest(AF_INET6)
                     .build();
 
-    private IkeSessionParams createIkeSessionParams(InetAddress mRemoteAddress) {
-        return new IkeSessionParams.Builder(sContext)
-                .setNetwork(mTunNetwork)
-                .setServerHostname(mRemoteAddress.getHostAddress())
-                .addSaProposal(SaProposalTest.buildIkeSaProposalWithNormalModeCipher())
-                .addSaProposal(SaProposalTest.buildIkeSaProposalWithCombinedModeCipher())
-                .setLocalIdentification(new IkeFqdnIdentification(LOCAL_HOSTNAME))
-                .setRemoteIdentification(new IkeFqdnIdentification(REMOTE_HOSTNAME))
-                .setAuthPsk(IKE_PSK)
-                .build();
-    }
-
-    private IkeSession openIkeSession(IkeSessionParams ikeParams) {
+    private IkeSession openIkeSessionWithRemoteAddress(InetAddress remoteAddress) {
+        IkeSessionParams ikeParams =
+                new IkeSessionParams.Builder(sContext)
+                        .setNetwork(mTunNetwork)
+                        .setServerHostname(remoteAddress.getHostAddress())
+                        .addSaProposal(SaProposalTest.buildIkeSaProposalWithNormalModeCipher())
+                        .addSaProposal(SaProposalTest.buildIkeSaProposalWithCombinedModeCipher())
+                        .setLocalIdentification(new IkeFqdnIdentification(LOCAL_HOSTNAME))
+                        .setRemoteIdentification(new IkeFqdnIdentification(REMOTE_HOSTNAME))
+                        .setAuthPsk(IKE_PSK)
+                        .build();
         return new IkeSession(
                 sContext,
                 ikeParams,
@@ -122,9 +120,11 @@
     }
 
     @Test
-    public void testIkeSessionSetupAndManageChildSas() throws Exception {
+    public void testIkeSessionSetupAndChildSessionSetupWithTunnelMode() throws Exception {
+        if (!hasTunnelsFeature()) return;
+
         // Open IKE Session
-        IkeSession ikeSession = openIkeSession(createIkeSessionParams(mRemoteAddress));
+        IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
         int expectedMsgId = 0;
         mTunUtils.awaitReqAndInjectResp(
                 IKE_INIT_SPI,
@@ -165,6 +165,9 @@
         assertTrue(firstChildConfig.getInternalDnsServers().isEmpty());
         assertTrue(firstChildConfig.getInternalDhcpServers().isEmpty());
 
+        assertNotNull(mFirstChildSessionCallback.awaitNextCreatedIpSecTransform());
+        assertNotNull(mFirstChildSessionCallback.awaitNextCreatedIpSecTransform());
+
         // Open additional Child Session
         TestChildSessionCallback additionalChildCb = new TestChildSessionCallback();
         ikeSession.openChildSession(CHILD_PARAMS, additionalChildCb);
@@ -181,9 +184,12 @@
                 Arrays.asList(EXPECTED_INBOUND_TS), firstChildConfig.getInboundTrafficSelectors());
         assertEquals(Arrays.asList(DEFAULT_V4_TS), firstChildConfig.getOutboundTrafficSelectors());
         assertTrue(additionalChildConfig.getInternalAddresses().isEmpty());
-        assertTrue(firstChildConfig.getInternalSubnets().isEmpty());
-        assertTrue(firstChildConfig.getInternalDnsServers().isEmpty());
-        assertTrue(firstChildConfig.getInternalDhcpServers().isEmpty());
+        assertTrue(additionalChildConfig.getInternalSubnets().isEmpty());
+        assertTrue(additionalChildConfig.getInternalDnsServers().isEmpty());
+        assertTrue(additionalChildConfig.getInternalDhcpServers().isEmpty());
+
+        assertNotNull(additionalChildCb.awaitNextCreatedIpSecTransform());
+        assertNotNull(additionalChildCb.awaitNextCreatedIpSecTransform());
 
         // Close additional Child Session
         ikeSession.closeChildSession(additionalChildCb);
@@ -193,6 +199,8 @@
                 true /* expectedUseEncap */,
                 hexStringToByteArray(SUCCESS_DELETE_CHILD_RESP));
 
+        assertNotNull(additionalChildCb.awaitNextDeletedIpSecTransform());
+        assertNotNull(additionalChildCb.awaitNextDeletedIpSecTransform());
         additionalChildCb.awaitOnClosed();
 
         // Close IKE Session
@@ -203,16 +211,20 @@
                 true /* expectedUseEncap */,
                 hexStringToByteArray(SUCCESS_DELETE_IKE_RESP));
 
+        assertNotNull(mFirstChildSessionCallback.awaitNextDeletedIpSecTransform());
+        assertNotNull(mFirstChildSessionCallback.awaitNextDeletedIpSecTransform());
         mFirstChildSessionCallback.awaitOnClosed();
         mIkeSessionCallback.awaitOnClosed();
 
-        // TODO: verify IpSecTransform pair is created and deleted
+        // TODO: verify created and deleted IpSecTransform pair and their directions
     }
 
     @Test
-    public void testIkeSessionKill() throws Exception {
+    public void testIkeSessionKillWithTunnelMode() throws Exception {
+        if (!hasTunnelsFeature()) return;
+
         // Open IKE Session
-        IkeSession ikeSession = openIkeSession(createIkeSessionParams(mRemoteAddress));
+        IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
         int expectedMsgId = 0;
         mTunUtils.awaitReqAndInjectResp(
                 IKE_INIT_SPI,
@@ -227,7 +239,6 @@
                 hexStringToByteArray(SUCCESS_IKE_AUTH_RESP));
 
         ikeSession.kill();
-
         mFirstChildSessionCallback.awaitOnClosed();
         mIkeSessionCallback.awaitOnClosed();
     }
@@ -238,7 +249,7 @@
                 "46B8ECA1E0D72A180000000000000000292022200000000000000024000000080000000E";
 
         // Open IKE Session
-        IkeSession ikeSession = openIkeSession(createIkeSessionParams(mRemoteAddress));
+        IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
         int expectedMsgId = 0;
         mTunUtils.awaitReqAndInjectResp(
                 IKE_INIT_SPI,
@@ -246,6 +257,8 @@
                 false /* expectedUseEncap */,
                 hexStringToByteArray(ikeInitFailRespHex));
 
+        mFirstChildSessionCallback.awaitOnClosed();
+
         IkeException exception = mIkeSessionCallback.awaitOnClosedException();
         assertNotNull(exception);
         assertTrue(exception instanceof IkeProtocolException);
@@ -254,5 +267,7 @@
         assertArrayEquals(EXPECTED_PROTOCOL_ERROR_DATA_NONE, protocolException.getErrorData());
     }
 
-    // TODO(b/148689509): Verify rekey process and handling IKE_AUTH failure
+    // TODO(b/155821007): Verify rekey process and handling IKE_AUTH failure
+
+    // TODO(b/155821007): Test creating transport mode Child SA
 }
diff --git a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java
index deba8fd..279d088 100644
--- a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java
+++ b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.app.AppOpsManager;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
 import android.net.InetAddresses;
 import android.net.IpSecTransform;
@@ -39,7 +40,6 @@
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.AppModeFull;
-import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -65,6 +65,13 @@
  *
  * <p>Subclasses MUST explicitly call #setUpTestNetwork and #tearDownTestNetwork to be able to use
  * the test network
+ *
+ * <p>All IKE Sessions running in test mode will generate SPIs deterministically. That is to say
+ * each IKE Session will always generate the same IKE INIT SPI and test vectors are generated based
+ * on this deterministic IKE SPI. Each test will use different local and remote addresses to avoid
+ * the case that the next test try to allocate the same SPI before the previous test has released
+ * it, since SPI resources are not released in testing thread. Similarly, each test MUST use
+ * different Network instances to avoid sharing the same IkeSocket and hitting IKE SPI collision.
  */
 @RunWith(AndroidJUnit4.class)
 @AppModeFull(reason = "MANAGE_TEST_NETWORKS permission can't be granted to instant apps")
@@ -116,7 +123,7 @@
         InstrumentationRegistry.getInstrumentation()
                 .getUiAutomation()
                 .adoptShellPermissionIdentity();
-        sTNM = (TestNetworkManager) sContext.getSystemService(Context.TEST_NETWORK_SERVICE);
+        sTNM = sContext.getSystemService(TestNetworkManager.class);
 
         // Under normal circumstances, the MANAGE_IPSEC_TUNNELS appop would be auto-granted, and
         // a standard permission is insufficient. So we shell out the appop, to give us the
@@ -149,10 +156,6 @@
     @After
     public void tearDown() throws Exception {
         tearDownTestNetwork();
-
-        resetNextAvailableAddress(NEXT_AVAILABLE_IP4_ADDR_LOCAL, INITIAL_AVAILABLE_IP4_ADDR_LOCAL);
-        resetNextAvailableAddress(
-                NEXT_AVAILABLE_IP4_ADDR_REMOTE, INITIAL_AVAILABLE_IP4_ADDR_REMOTE);
     }
 
     void setUpTestNetwork(InetAddress localAddr) throws Exception {
@@ -185,9 +188,8 @@
                             pkg, // Package name
                             opName, // Appop
                             (allow ? "allow" : "deny")); // Action
-            Log.d("IKE", "CTS setAppOp cmd " + cmd);
 
-            String result = SystemUtil.runShellCommand(cmd);
+            SystemUtil.runShellCommand(cmd);
         }
     }
 
@@ -229,6 +231,7 @@
         }
     }
 
+    /** Testing callback that allows caller to block current thread until a method get called */
     static class TestIkeSessionCallback implements IkeSessionCallback {
         private CompletableFuture<IkeSessionConfiguration> mFutureIkeConfig =
                 new CompletableFuture<>();
@@ -282,6 +285,7 @@
         }
     }
 
+    /** Testing callback that allows caller to block current thread until a method get called */
     static class TestChildSessionCallback implements ChildSessionCallback {
         private CompletableFuture<ChildSessionConfiguration> mFutureChildConfig =
                 new CompletableFuture<>();
@@ -368,6 +372,11 @@
         }
     }
 
+    /** Package private method to check if device has IPsec tunnels feature */
+    static boolean hasTunnelsFeature() {
+        return sContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS);
+    }
+
     // TODO(b/148689509): Verify IKE Session setup using EAP and digital-signature-based auth
 
     // TODO(b/148689509): Verify hostname based creation
diff --git a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java
index 5a8258d..f52b88b 100644
--- a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java
+++ b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java
@@ -121,7 +121,9 @@
                             + " and message ID "
                             + expectedMsgId);
         }
-        return null;
+
+        throw new IllegalStateException(
+                "Hit an impossible case where fail() didn't throw an exception");
     }
 
     private static boolean isIke(
diff --git a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
index 4418e17..0816aba 100644
--- a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
+++ b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
@@ -16,6 +16,7 @@
 
 package android.net.cts
 
+import android.Manifest.permission.CONNECTIVITY_INTERNAL
 import android.Manifest.permission.NETWORK_SETTINGS
 import android.Manifest.permission.READ_DEVICE_CONFIG
 import android.Manifest.permission.WRITE_DEVICE_CONFIG
@@ -31,6 +32,7 @@
 import android.net.Uri
 import android.net.cts.util.CtsNetUtils
 import android.net.wifi.WifiManager
+import android.os.Build
 import android.os.ConditionVariable
 import android.platform.test.annotations.AppModeFull
 import android.provider.DeviceConfig
@@ -164,7 +166,10 @@
                     "access."
             assertNotEquals(network, cm.activeNetwork, wifiDefaultMessage)
 
-            doAsShell(NETWORK_SETTINGS) { cm.startCaptivePortalApp(network) }
+            val startPortalAppPermission =
+                    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) CONNECTIVITY_INTERNAL
+                    else NETWORK_SETTINGS
+            doAsShell(startPortalAppPermission) { cm.startCaptivePortalApp(network) }
             assertTrue(portalContentRequestCv.block(TEST_TIMEOUT_MS), "The captive portal login " +
                     "page was still not fetched ${TEST_TIMEOUT_MS}ms after startCaptivePortalApp.")
 
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 1ee08ff..d498ed9 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -16,13 +16,17 @@
 
 package android.net.cts;
 
+import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
 import static android.content.pm.PackageManager.FEATURE_ETHERNET;
 import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
-import static android.content.pm.PackageManager.FEATURE_WIFI;
 import static android.content.pm.PackageManager.FEATURE_USB_HOST;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.cts.util.CtsNetUtils.ConnectivityActionReceiver;
 import static android.net.cts.util.CtsNetUtils.HTTP_PORT;
@@ -36,6 +40,16 @@
 import static android.system.OsConstants.AF_UNSPEC;
 
 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
 
 import android.annotation.NonNull;
 import android.app.Instrumentation;
@@ -45,6 +59,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
@@ -59,10 +74,12 @@
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkInfo.State;
 import android.net.NetworkRequest;
+import android.net.NetworkUtils;
 import android.net.SocketKeepalive;
 import android.net.cts.util.CtsNetUtils;
 import android.net.util.KeepaliveUtils;
 import android.net.wifi.WifiManager;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Looper;
 import android.os.MessageQueue;
@@ -71,15 +88,22 @@
 import android.os.VintfRuntimeInfo;
 import android.platform.test.annotations.AppModeFull;
 import android.provider.Settings;
-import android.test.AndroidTestCase;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.ArrayUtils;
 
 import libcore.io.Streams;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.InputStream;
@@ -105,7 +129,8 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-public class ConnectivityManagerTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class ConnectivityManagerTest {
 
     private static final String TAG = ConnectivityManagerTest.class.getSimpleName();
 
@@ -117,7 +142,10 @@
     private static final int INTERVAL_KEEPALIVE_RETRY_MS = 500;
     private static final int MAX_KEEPALIVE_RETRY_COUNT = 3;
     private static final int MIN_KEEPALIVE_INTERVAL = 10;
-    private static final int NETWORK_CHANGE_METEREDNESS_TIMEOUT = 5000;
+
+    // Changing meteredness on wifi involves reconnecting, which can take several seconds (involves
+    // re-associating, DHCP...)
+    private static final int NETWORK_CHANGE_METEREDNESS_TIMEOUT = 30_000;
     private static final int NUM_TRIES_MULTIPATH_PREF_CHECK = 20;
     private static final long INTERVAL_MULTIPATH_PREF_CHECK_MS = 500;
     // device could have only one interface: data, wifi.
@@ -141,22 +169,19 @@
     private PackageManager mPackageManager;
     private final HashMap<Integer, NetworkConfig> mNetworks =
             new HashMap<Integer, NetworkConfig>();
-    boolean mWifiConnectAttempted;
+    boolean mWifiWasDisabled;
     private UiAutomation mUiAutomation;
     private CtsNetUtils mCtsNetUtils;
-    private boolean mShellPermissionIdentityAdopted;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        Looper.prepare();
-        mContext = getContext();
+    @Before
+    public void setUp() throws Exception {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = mInstrumentation.getContext();
         mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
         mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
         mPackageManager = mContext.getPackageManager();
         mCtsNetUtils = new CtsNetUtils(mContext);
-        mWifiConnectAttempted = false;
+        mWifiWasDisabled = false;
 
         // Get com.android.internal.R.array.networkAttributes
         int resId = mContext.getResources().getIdentifier("networkAttributes", "array", "android");
@@ -173,20 +198,17 @@
             } catch (Exception e) {}
         }
         mUiAutomation = mInstrumentation.getUiAutomation();
-        mShellPermissionIdentityAdopted = false;
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         // Return WiFi to its original disabled state after tests that explicitly connect.
-        if (mWifiConnectAttempted) {
+        if (mWifiWasDisabled) {
             mCtsNetUtils.disconnectFromWifi(null);
         }
         if (mCtsNetUtils.cellConnectAttempted()) {
             mCtsNetUtils.disconnectFromCell();
         }
-        dropShellPermissionIdentity();
-        super.tearDown();
     }
 
     /**
@@ -195,13 +217,12 @@
      * automatically in tearDown().
      */
     private Network ensureWifiConnected() {
-        if (mWifiManager.isWifiEnabled()) {
-            return mCtsNetUtils.getWifiNetwork();
-        }
-        mWifiConnectAttempted = true;
+        mWifiWasDisabled = !mWifiManager.isWifiEnabled();
+        // Even if wifi is enabled, the network may not be connected or ready yet
         return mCtsNetUtils.connectToWifi();
     }
 
+    @Test
     public void testIsNetworkTypeValid() {
         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE));
         assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_WIFI));
@@ -231,12 +252,14 @@
 
     }
 
+    @Test
     public void testSetNetworkPreference() {
         // getNetworkPreference() and setNetworkPreference() are both deprecated so they do
         // not preform any action.  Verify they are at least still callable.
         mCm.setNetworkPreference(mCm.getNetworkPreference());
     }
 
+    @Test
     public void testGetActiveNetworkInfo() {
         NetworkInfo ni = mCm.getActiveNetworkInfo();
 
@@ -245,6 +268,7 @@
         assertTrue(ni.getState() == State.CONNECTED);
     }
 
+    @Test
     public void testGetActiveNetwork() {
         Network network = mCm.getActiveNetwork();
         assertNotNull("You must have an active network connection to complete CTS", network);
@@ -257,6 +281,7 @@
         assertTrue(ni.getState() == State.CONNECTED);
     }
 
+    @Test
     public void testGetNetworkInfo() {
         for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE+1; type++) {
             if (shouldBeSupported(type)) {
@@ -275,6 +300,7 @@
         }
     }
 
+    @Test
     public void testGetAllNetworkInfo() {
         NetworkInfo[] ni = mCm.getAllNetworkInfo();
         assertTrue(ni.length >= MIN_NUM_NETWORK_TYPES);
@@ -298,6 +324,7 @@
      * and that they are made from different IP addresses.
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testOpenConnection() throws Exception {
         boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
                 && mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
@@ -377,6 +404,7 @@
         } catch (UnsupportedOperationException expected) {}
     }
 
+    @Test
     public void testStartUsingNetworkFeature() {
 
         final String invalidateFeature = "invalidateFeature";
@@ -406,6 +434,7 @@
                (networkType == ConnectivityManager.TYPE_ETHERNET && shouldEthernetBeSupported());
     }
 
+    @Test
     public void testIsNetworkSupported() {
         for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
             boolean supported = mCm.isNetworkSupported(type);
@@ -417,12 +446,14 @@
         }
     }
 
+    @Test
     public void testRequestRouteToHost() {
         for (int type = -1 ; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
             assertRequestRouteToHostUnsupported(type, HOST_ADDRESS);
         }
     }
 
+    @Test
     public void testTest() {
         mCm.getBackgroundDataSetting();
     }
@@ -443,6 +474,7 @@
      * that it would increase test coverage by much (how many devices have 3G radio but not Wifi?).
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testRegisterNetworkCallback() {
         if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
@@ -484,6 +516,7 @@
      * of a {@code NetworkCallback}.
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testRegisterNetworkCallback_withPendingIntent() {
         if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
@@ -529,6 +562,7 @@
      * see if we get a callback for an INTERNET request.
      */
     @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
+    @Test
     public void testRequestNetworkCallback() {
         final TestNetworkCallback callback = new TestNetworkCallback();
         mCm.requestNetwork(new NetworkRequest.Builder()
@@ -552,6 +586,7 @@
      * fail. Use WIFI and switch Wi-Fi off.
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testRequestNetworkCallback_onUnavailable() {
         final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
         if (previousWifiEnabledState) {
@@ -589,6 +624,7 @@
 
     /** Verify restricted networks cannot be requested. */
     @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
+    @Test
     public void testRestrictedNetworks() {
         // Verify we can request unrestricted networks:
         NetworkRequest request = new NetworkRequest.Builder()
@@ -710,6 +746,7 @@
      * for metered and unmetered networks.
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testGetMultipathPreference() throws Exception {
         final ContentResolver resolver = mContext.getContentResolver();
         ensureWifiConnected();
@@ -878,18 +915,6 @@
                 keepalivesPerTransport, nc);
     }
 
-    private void adoptShellPermissionIdentity() {
-        mUiAutomation.adoptShellPermissionIdentity();
-        mShellPermissionIdentityAdopted = true;
-    }
-
-    private void dropShellPermissionIdentity() {
-        if (mShellPermissionIdentityAdopted) {
-            mUiAutomation.dropShellPermissionIdentity();
-            mShellPermissionIdentityAdopted = false;
-        }
-    }
-
     private static boolean isTcpKeepaliveSupportedByKernel() {
         final String kVersionString = VintfRuntimeInfo.getKernelVersion();
         return compareMajorMinorVersion(kVersionString, "4.8") >= 0;
@@ -924,6 +949,7 @@
      * Verifies that version string compare logic returns expected result for various cases.
      * Note that only major and minor number are compared.
      */
+    @Test
     public void testMajorMinorVersionCompare() {
         assertEquals(0, compareMajorMinorVersion("4.8.1", "4.8"));
         assertEquals(1, compareMajorMinorVersion("4.9", "4.8.1"));
@@ -943,6 +969,7 @@
      * keepalives is set to 0.
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testKeepaliveWifiUnsupported() throws Exception {
         if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testKeepaliveUnsupported cannot execute unless device"
@@ -952,32 +979,36 @@
 
         final Network network = ensureWifiConnected();
         if (getSupportedKeepalivesForNet(network) != 0) return;
+        final InetAddress srcAddr = getFirstV4Address(network);
+        assumeTrue("This test requires native IPv4", srcAddr != null);
 
-        adoptShellPermissionIdentity();
-
-        assertEquals(0, createConcurrentSocketKeepalives(network, 1, 0));
-        assertEquals(0, createConcurrentSocketKeepalives(network, 0, 1));
-
-        dropShellPermissionIdentity();
+        runWithShellPermissionIdentity(() -> {
+            assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 1, 0));
+            assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 0, 1));
+        });
     }
 
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testCreateTcpKeepalive() throws Exception {
         if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testCreateTcpKeepalive cannot execute unless device supports WiFi");
             return;
         }
 
-        adoptShellPermissionIdentity();
-
         final Network network = ensureWifiConnected();
         if (getSupportedKeepalivesForNet(network) == 0) return;
+        final InetAddress srcAddr = getFirstV4Address(network);
+        assumeTrue("This test requires native IPv4", srcAddr != null);
+
         // If kernel < 4.8 then it doesn't support TCP keepalive, but it might still support
         // NAT-T keepalive. If keepalive limits from resource overlay is not zero, TCP keepalive
         // needs to be supported except if the kernel doesn't support it.
         if (!isTcpKeepaliveSupportedByKernel()) {
             // Sanity check to ensure the callback result is expected.
-            assertEquals(0, createConcurrentSocketKeepalives(network, 0, 1));
+            runWithShellPermissionIdentity(() -> {
+                assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 0, 1));
+            });
             Log.i(TAG, "testCreateTcpKeepalive is skipped for kernel "
                     + VintfRuntimeInfo.getKernelVersion());
             return;
@@ -991,6 +1022,8 @@
             // Should able to start keep alive offload when socket is idle.
             final Executor executor = mContext.getMainExecutor();
             final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
+
+            mUiAutomation.adoptShellPermissionIdentity();
             try (SocketKeepalive sk = mCm.createSocketKeepalive(network, s, executor, callback)) {
                 sk.start(MIN_KEEPALIVE_INTERVAL);
                 callback.expectStarted();
@@ -1012,6 +1045,8 @@
                 // Stop.
                 sk.stop();
                 callback.expectStopped();
+            } finally {
+                mUiAutomation.dropShellPermissionIdentity();
             }
 
             // Ensure socket is still connected.
@@ -1040,9 +1075,12 @@
 
             // Should get ERROR_SOCKET_NOT_IDLE because there is still data in the receive queue
             // that has not been read.
+            mUiAutomation.adoptShellPermissionIdentity();
             try (SocketKeepalive sk = mCm.createSocketKeepalive(network, s, executor, callback)) {
                 sk.start(MIN_KEEPALIVE_INTERVAL);
                 callback.expectError(SocketKeepalive.ERROR_SOCKET_NOT_IDLE);
+            } finally {
+                mUiAutomation.dropShellPermissionIdentity();
             }
         }
     }
@@ -1087,7 +1125,7 @@
     }
 
     private @NonNull ArrayList<SocketKeepalive> createConcurrentNattSocketKeepalives(
-            @NonNull Network network, int requestCount,
+            @NonNull Network network, @NonNull InetAddress srcAddr, int requestCount,
             @NonNull TestSocketKeepaliveCallback callback)  throws Exception {
 
         final Executor executor = mContext.getMainExecutor();
@@ -1095,7 +1133,6 @@
         // Initialize a real NaT-T socket.
         final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
         final UdpEncapsulationSocket nattSocket = mIpSec.openUdpEncapsulationSocket();
-        final InetAddress srcAddr = getFirstV4Address(network);
         final InetAddress dstAddr = getAddrByName(TEST_HOST, AF_INET);
         assertNotNull(srcAddr);
         assertNotNull(dstAddr);
@@ -1136,11 +1173,12 @@
      * @return the total number of keepalives created.
      */
     private int createConcurrentSocketKeepalives(
-            @NonNull Network network, int nattCount, int tcpCount) throws Exception {
+            @NonNull Network network, @NonNull InetAddress srcAddr, int nattCount, int tcpCount)
+            throws Exception {
         final ArrayList<SocketKeepalive> kalist = new ArrayList<>();
         final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
 
-        kalist.addAll(createConcurrentNattSocketKeepalives(network, nattCount, callback));
+        kalist.addAll(createConcurrentNattSocketKeepalives(network, srcAddr, nattCount, callback));
         kalist.addAll(createConcurrentTcpSocketKeepalives(network, tcpCount, callback));
 
         final int ret = kalist.size();
@@ -1160,6 +1198,7 @@
      * get leaked after iterations.
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testSocketKeepaliveLimitWifi() throws Exception {
         if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testSocketKeepaliveLimitWifi cannot execute unless device"
@@ -1172,33 +1211,39 @@
         if (supported == 0) {
             return;
         }
+        final InetAddress srcAddr = getFirstV4Address(network);
+        assumeTrue("This test requires native IPv4", srcAddr != null);
 
-        adoptShellPermissionIdentity();
+        runWithShellPermissionIdentity(() -> {
+            // Verifies that the supported keepalive slots meet MIN_SUPPORTED_KEEPALIVE_COUNT.
+            assertGreaterOrEqual(supported, MIN_SUPPORTED_WIFI_KEEPALIVE_COUNT);
 
-        // Verifies that the supported keepalive slots meet MIN_SUPPORTED_KEEPALIVE_COUNT.
-        assertGreaterOrEqual(supported, MIN_SUPPORTED_WIFI_KEEPALIVE_COUNT);
-
-        // Verifies that Nat-T keepalives can be established.
-        assertEquals(supported, createConcurrentSocketKeepalives(network, supported + 1, 0));
-        // Verifies that keepalives don't get leaked in second round.
-        assertEquals(supported, createConcurrentSocketKeepalives(network, supported, 0));
+            // Verifies that Nat-T keepalives can be established.
+            assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
+                    supported + 1, 0));
+            // Verifies that keepalives don't get leaked in second round.
+            assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, supported,
+                    0));
+        });
 
         // If kernel < 4.8 then it doesn't support TCP keepalive, but it might still support
         // NAT-T keepalive. Test below cases only if TCP keepalive is supported by kernel.
-        if (isTcpKeepaliveSupportedByKernel()) {
-            assertEquals(supported, createConcurrentSocketKeepalives(network, 0, supported + 1));
+        if (!isTcpKeepaliveSupportedByKernel()) return;
+
+        runWithShellPermissionIdentity(() -> {
+            assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, 0,
+                    supported + 1));
 
             // Verifies that different types can be established at the same time.
-            assertEquals(supported, createConcurrentSocketKeepalives(network,
+            assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
                     supported / 2, supported - supported / 2));
 
             // Verifies that keepalives don't get leaked in second round.
-            assertEquals(supported, createConcurrentSocketKeepalives(network, 0, supported));
-            assertEquals(supported, createConcurrentSocketKeepalives(network,
+            assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, 0,
+                    supported));
+            assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
                     supported / 2, supported - supported / 2));
-        }
-
-        dropShellPermissionIdentity();
+        });
     }
 
     /**
@@ -1206,6 +1251,7 @@
      * don't get leaked after iterations.
      */
     @AppModeFull(reason = "Cannot request network in instant app mode")
+    @Test
     public void testSocketKeepaliveLimitTelephony() throws Exception {
         if (!mPackageManager.hasSystemFeature(FEATURE_TELEPHONY)) {
             Log.i(TAG, "testSocketKeepaliveLimitTelephony cannot execute unless device"
@@ -1222,18 +1268,19 @@
 
         final Network network = mCtsNetUtils.connectToCell();
         final int supported = getSupportedKeepalivesForNet(network);
+        final InetAddress srcAddr = getFirstV4Address(network);
+        assumeTrue("This test requires native IPv4", srcAddr != null);
 
-        adoptShellPermissionIdentity();
-
-        // Verifies that the supported keepalive slots meet minimum requirement.
-        assertGreaterOrEqual(supported, MIN_SUPPORTED_CELLULAR_KEEPALIVE_COUNT);
-
-        // Verifies that Nat-T keepalives can be established.
-        assertEquals(supported, createConcurrentSocketKeepalives(network, supported + 1, 0));
-        // Verifies that keepalives don't get leaked in second round.
-        assertEquals(supported, createConcurrentSocketKeepalives(network, supported, 0));
-
-        dropShellPermissionIdentity();
+        runWithShellPermissionIdentity(() -> {
+            // Verifies that the supported keepalive slots meet minimum requirement.
+            assertGreaterOrEqual(supported, MIN_SUPPORTED_CELLULAR_KEEPALIVE_COUNT);
+            // Verifies that Nat-T keepalives can be established.
+            assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
+                    supported + 1, 0));
+            // Verifies that keepalives don't get leaked in second round.
+            assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, supported,
+                    0));
+        });
     }
 
     private int getIntResourceForName(@NonNull String resName) {
@@ -1246,6 +1293,7 @@
      * Verifies that the keepalive slots are limited as customized for unprivileged requests.
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
     public void testSocketKeepaliveUnprivileged() throws Exception {
         if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testSocketKeepaliveUnprivileged cannot execute unless device"
@@ -1258,6 +1306,8 @@
         if (supported == 0) {
             return;
         }
+        final InetAddress srcAddr = getFirstV4Address(network);
+        assumeTrue("This test requires native IPv4", srcAddr != null);
 
         // Resource ID might be shifted on devices that compiled with different symbols.
         // Thus, resolve ID at runtime is needed.
@@ -1273,11 +1323,46 @@
         final int expectedUnprivileged =
                 Math.min(allowedUnprivilegedPerUid, supported - reservedPrivilegedSlots);
         assertEquals(expectedUnprivileged,
-                createConcurrentSocketKeepalives(network, supported + 1, 0));
+                createConcurrentSocketKeepalives(network, srcAddr, supported + 1, 0));
     }
 
     private static void assertGreaterOrEqual(long greater, long lesser) {
         assertTrue("" + greater + " expected to be greater than or equal to " + lesser,
                 greater >= lesser);
     }
+
+    /**
+     * Verifies that apps are not allowed to access restricted networks even if they declare the
+     * CONNECTIVITY_USE_RESTRICTED_NETWORKS permission in their manifests.
+     * See. b/144679405.
+     */
+    @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
+    public void testRestrictedNetworkPermission() throws Exception {
+        // Ensure that CONNECTIVITY_USE_RESTRICTED_NETWORKS isn't granted to this package.
+        final PackageInfo app = mPackageManager.getPackageInfo(mContext.getPackageName(),
+                GET_PERMISSIONS);
+        final int index = ArrayUtils.indexOf(
+                app.requestedPermissions, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+        assertTrue(index >= 0);
+        assertTrue(app.requestedPermissionsFlags[index] != PERMISSION_GRANTED);
+
+        // Ensure that NetworkUtils.queryUserAccess always returns false since this package should
+        // not have netd system permission to call this function.
+        final Network wifiNetwork = ensureWifiConnected();
+        assertFalse(NetworkUtils.queryUserAccess(Binder.getCallingUid(), wifiNetwork.netId));
+
+        // Ensure that this package cannot bind to any restricted network that's currently
+        // connected.
+        Network[] networks = mCm.getAllNetworks();
+        for (Network network : networks) {
+            NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
+            if (nc != null && !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
+                try {
+                    network.bindSocket(new Socket());
+                    fail("Bind to restricted network " + network + " unexpectedly succeeded");
+                } catch (IOException expected) {}
+            }
+        }
+    }
 }
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 03b961b..2824db7 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -592,4 +592,50 @@
             assertNull(it.uri)
         }
     }
+
+    @Test
+    fun testTemporarilyUnmeteredCapability() {
+        // This test will create a networks with/without NET_CAPABILITY_TEMPORARILY_NOT_METERED
+        // and check that the callback reflects the capability changes.
+        // First create a request to make sure the network is kept up
+        val request1 = NetworkRequest.Builder()
+                .clearCapabilities()
+                .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+                .build()
+        val callback1 = TestableNetworkCallback(DEFAULT_TIMEOUT_MS).also {
+            registerNetworkCallback(request1, it)
+        }
+        requestNetwork(request1, callback1)
+
+        // Then file the interesting request
+        val request = NetworkRequest.Builder()
+                .clearCapabilities()
+                .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+                .build()
+        val callback = TestableNetworkCallback()
+        requestNetwork(request, callback)
+
+        // Connect the network
+        createConnectedNetworkAgent().let { (agent, _) ->
+            callback.expectAvailableThenValidatedCallbacks(agent.network)
+
+            // Send TEMP_NOT_METERED and check that the callback is called appropriately.
+            val nc1 = NetworkCapabilities(agent.nc)
+                    .addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+            agent.sendNetworkCapabilities(nc1)
+            callback.expectCapabilitiesThat(agent.network) {
+                it.hasCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+            }
+
+            // Remove TEMP_NOT_METERED and check that the callback is called appropriately.
+            val nc2 = NetworkCapabilities(agent.nc)
+                    .removeCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+            agent.sendNetworkCapabilities(nc2)
+            callback.expectCapabilitiesThat(agent.network) {
+                !it.hasCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+            }
+        }
+
+        // tearDown() will unregister the requests and agents
+    }
 }
diff --git a/tests/cts/net/src/android/net/cts/NetworkRequestTest.java b/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
index f32ee9e..d118c8a 100644
--- a/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
@@ -18,6 +18,7 @@
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
 import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
@@ -86,6 +87,16 @@
         verifyNoCapabilities(nr);
     }
 
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testTemporarilyNotMeteredCapability() {
+        assertTrue(new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED).build()
+                .hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+        assertFalse(new NetworkRequest.Builder()
+                .removeCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED).build()
+                .hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+    }
+
     private void verifyNoCapabilities(NetworkRequest nr) {
         // NetworkCapabilities.mNetworkCapabilities is defined as type long
         final int MAX_POSSIBLE_CAPABILITY = Long.SIZE;
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
index df2de4f..f39b184 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -17,6 +17,7 @@
 package android.net.cts.util;
 
 import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 
@@ -60,7 +61,6 @@
     private static final int DURATION = 10000;
     private static final int SOCKET_TIMEOUT_MS = 2000;
     private static final int PRIVATE_DNS_PROBE_MS = 1_000;
-    private static final int PRIVATE_DNS_INTERVAL_MS = 500;
 
     public static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 6_000;
     public static final int HTTP_PORT = 80;
@@ -262,6 +262,15 @@
         mOldPrivateDnsMode = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_MODE);
         mOldPrivateDnsSpecifier = Settings.Global.getString(mCR,
                 Settings.Global.PRIVATE_DNS_SPECIFIER);
+        // It's possible that there is no private DNS default value in Settings.
+        // Give it a proper default mode which is opportunistic mode.
+        if (mOldPrivateDnsMode == null) {
+            mOldPrivateDnsSpecifier = "";
+            mOldPrivateDnsMode = PRIVATE_DNS_MODE_OPPORTUNISTIC;
+            Settings.Global.putString(mCR,
+                    Settings.Global.PRIVATE_DNS_SPECIFIER, mOldPrivateDnsSpecifier);
+            Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, mOldPrivateDnsMode);
+        }
     }
 
     public void restorePrivateDnsSetting() throws InterruptedException {
diff --git a/tests/cts/tethering/AndroidTest.xml b/tests/cts/tethering/AndroidTest.xml
index d0a2bce..e752e3a 100644
--- a/tests/cts/tethering/AndroidTest.xml
+++ b/tests/cts/tethering/AndroidTest.xml
@@ -28,4 +28,8 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.tethering.cts" />
     </test>
+
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+        <option name="mainline-module-package-name" value="com.google.android.tethering" />
+    </object>
 </configuration>