Merge Android R (rvc-dev-plus-aosp-without-vendor@6692709)

Bug: 166295507
Merged-In: Id9ba142af9d2da12bda66d0c376d4b8c13f1503b
Change-Id: I1a1bdaabd06f43b695ac94172a6f46d00b7f5b84
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
index 219cc3d..bb492a1 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -67,7 +67,7 @@
         setAppIdle(true);
         launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
         finishActivity();
-        assertAppIdle(false); // Sanity check - not idle anymore, since activity was launched...
+        assertAppIdle(false); // verify - not idle anymore, since activity was launched...
         assertBackgroundNetworkAccess(true);
         setAppIdle(true);
         assertBackgroundNetworkAccess(false);
@@ -86,29 +86,29 @@
         assertBackgroundNetworkAccess(false);
 
         addPowerSaveModeWhitelist(TEST_APP2_PKG);
-        assertAppIdle(false); // Sanity check - not idle anymore, since whitelisted
+        assertAppIdle(false); // verify - not idle anymore, since whitelisted
         assertBackgroundNetworkAccess(true);
 
         setAppIdleNoAssert(true);
         assertAppIdle(false); // app is still whitelisted
         removePowerSaveModeWhitelist(TEST_APP2_PKG);
-        assertAppIdle(true); // Sanity check - idle again, once whitelisted was removed
+        assertAppIdle(true); // verify - idle again, once whitelisted was removed
         assertBackgroundNetworkAccess(false);
 
         setAppIdle(true);
         addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
-        assertAppIdle(false); // Sanity check - not idle anymore, since whitelisted
+        assertAppIdle(false); // verify - not idle anymore, since whitelisted
         assertBackgroundNetworkAccess(true);
 
         setAppIdleNoAssert(true);
         assertAppIdle(false); // app is still whitelisted
         removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
-        assertAppIdle(true); // Sanity check - idle again, once whitelisted was removed
+        assertAppIdle(true); // verify - idle again, once whitelisted was removed
         assertBackgroundNetworkAccess(false);
 
         assertsForegroundAlwaysHasNetworkAccess();
 
-        // Sanity check - no whitelist, no access!
+        // verify - no whitelist, no access!
         setAppIdle(true);
         assertBackgroundNetworkAccess(false);
     }
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index e5fd149..71f6f2f 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -229,12 +229,12 @@
     }
 
     protected void assertBackgroundNetworkAccess(boolean expectAllowed) throws Exception {
-        assertBackgroundState(); // Sanity check.
+        assertBackgroundState();
         assertNetworkAccess(expectAllowed /* expectAvailable */, false /* needScreenOn */);
     }
 
     protected void assertForegroundNetworkAccess() throws Exception {
-        assertForegroundState(); // Sanity check.
+        assertForegroundState();
         // We verified that app is in foreground state but if the screen turns-off while
         // verifying for network access, the app will go into background state (in case app's
         // foreground status was due to top activity). So, turn the screen on when verifying
@@ -243,7 +243,7 @@
     }
 
     protected void assertForegroundServiceNetworkAccess() throws Exception {
-        assertForegroundServiceState(); // Sanity check.
+        assertForegroundServiceState();
         assertNetworkAccess(true /* expectAvailable */, false /* needScreenOn */);
     }
 
@@ -374,7 +374,7 @@
         }
         // Network status format is described on MyBroadcastReceiver.checkNetworkStatus()
         final String[] parts = resultData.split(NETWORK_STATUS_SEPARATOR);
-        assertEquals("Wrong network status: " + resultData, 5, parts.length); // Sanity check
+        assertEquals("Wrong network status: " + resultData, 5, parts.length);
         final State state = parts[0].equals("null") ? null : State.valueOf(parts[0]);
         final DetailedState detailedState = parts[1].equals("null")
                 ? null : DetailedState.valueOf(parts[1]);
@@ -547,7 +547,7 @@
         // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
         // need to use netpolicy for whitelisting
         executeShellCommand("dumpsys deviceidle whitelist +" + packageName);
-        assertPowerSaveModeWhitelist(packageName, true); // Sanity check
+        assertPowerSaveModeWhitelist(packageName, true);
     }
 
     protected void removePowerSaveModeWhitelist(String packageName) throws Exception {
@@ -555,7 +555,7 @@
         // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
         // need to use netpolicy for whitelisting
         executeShellCommand("dumpsys deviceidle whitelist -" + packageName);
-        assertPowerSaveModeWhitelist(packageName, false); // Sanity check
+        assertPowerSaveModeWhitelist(packageName, false);
     }
 
     protected void assertPowerSaveModeExceptIdleWhitelist(String packageName, boolean expected)
@@ -571,7 +571,7 @@
         // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
         // need to use netpolicy for whitelisting
         executeShellCommand("dumpsys deviceidle except-idle-whitelist +" + packageName);
-        assertPowerSaveModeExceptIdleWhitelist(packageName, true); // Sanity check
+        assertPowerSaveModeExceptIdleWhitelist(packageName, true);
     }
 
     protected void removePowerSaveModeExceptIdleWhitelist(String packageName) throws Exception {
@@ -580,7 +580,7 @@
         // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
         // need to use netpolicy for whitelisting
         executeShellCommand("dumpsys deviceidle except-idle-whitelist reset");
-        assertPowerSaveModeExceptIdleWhitelist(packageName, false); // Sanity check
+        assertPowerSaveModeExceptIdleWhitelist(packageName, false);
     }
 
     protected void turnBatteryOn() throws Exception {
@@ -635,7 +635,7 @@
     }
 
     protected void setDozeMode(boolean enabled) throws Exception {
-        // Sanity check, since tests should check beforehand....
+        // Check doze mode is supported.
         assertTrue("Device does not support Doze Mode", isDozeModeSupported());
 
         Log.i(TAG, "Setting Doze Mode to " + enabled);
@@ -648,7 +648,6 @@
             turnBatteryOff();
             executeShellCommand("dumpsys deviceidle unforce");
         }
-        // Sanity check.
         assertDozeMode(enabled);
     }
 
@@ -659,7 +658,7 @@
     protected void setAppIdle(boolean enabled) throws Exception {
         Log.i(TAG, "Setting app idle to " + enabled);
         executeSilentShellCommand("am set-inactive " + TEST_APP2_PKG + " " + enabled );
-        assertAppIdle(enabled); // Sanity check
+        assertAppIdle(enabled);
     }
 
     protected void setAppIdleNoAssert(boolean enabled) throws Exception {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
index aa2c914..604a0b6 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
@@ -67,7 +67,7 @@
     public void testGetRestrictBackgroundStatus_disabled() throws Exception {
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
 
-        // Sanity check: make sure status is always disabled, never whitelisted
+        // Verify status is always disabled, never whitelisted
         addRestrictBackgroundWhitelist(mUid);
         assertRestrictBackgroundChangedReceived(0);
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
index 4598c39..ac28c7a 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -367,7 +367,7 @@
         // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
         // need to use netpolicy for whitelisting
         runCommand("dumpsys deviceidle whitelist +" + packageName);
-        assertPowerSaveModeWhitelist(packageName, true); // Sanity check
+        assertPowerSaveModeWhitelist(packageName, true);
     }
 
     protected boolean isDozeModeEnabled() throws Exception {
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index fdc54bd..4c00428 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -47,7 +47,6 @@
         "ctstestserver",
         "junit",
         "junit-params",
-        "libnanohttpd",
         "mockwebserver",
         "net-utils-framework-common",
         "truth-prebuilt",
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 a81063b..6264cea 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
@@ -56,7 +56,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.compatibility.common.util.SystemUtil;
-import com.android.testutils.ArrayTrackRecord;
+import com.android.net.module.util.ArrayTrackRecord;
 
 import org.junit.After;
 import org.junit.AfterClass;
diff --git a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
index 4a7d38a..12a966f 100644
--- a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
+++ b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
@@ -19,7 +19,6 @@
 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
 import android.content.pm.PackageManager.FEATURE_TELEPHONY
 import android.content.pm.PackageManager.FEATURE_WIFI
 import android.net.ConnectivityManager
@@ -30,20 +29,25 @@
 import android.net.NetworkCapabilities.TRANSPORT_WIFI
 import android.net.NetworkRequest
 import android.net.Uri
+import android.net.cts.NetworkValidationTestUtil.clearValidationTestUrlsDeviceConfig
+import android.net.cts.NetworkValidationTestUtil.runAsShell
+import android.net.cts.NetworkValidationTestUtil.setHttpUrlDeviceConfig
+import android.net.cts.NetworkValidationTestUtil.setHttpsUrlDeviceConfig
+import android.net.cts.NetworkValidationTestUtil.setUrlExpirationDeviceConfig
+import com.android.testutils.TestHttpServer.Request
 import android.net.cts.util.CtsNetUtils
+import android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL
+import android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL
 import android.net.wifi.WifiManager
 import android.os.Build
-import android.os.ConditionVariable
 import android.platform.test.annotations.AppModeFull
 import android.provider.DeviceConfig
 import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
 import android.text.TextUtils
 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
 import androidx.test.runner.AndroidJUnit4
-import com.android.compatibility.common.util.SystemUtil
+import com.android.testutils.TestHttpServer
 import com.android.testutils.isDevSdkInRange
-import fi.iki.elonen.NanoHTTPD
-import fi.iki.elonen.NanoHTTPD.Response.IStatus
 import fi.iki.elonen.NanoHTTPD.Response.Status
 import junit.framework.AssertionFailedError
 import org.junit.After
@@ -55,15 +59,12 @@
 import java.util.concurrent.TimeoutException
 import kotlin.test.Test
 import kotlin.test.assertNotEquals
+import kotlin.test.assertNotNull
 import kotlin.test.assertTrue
 
-private const val TEST_CAPTIVE_PORTAL_HTTPS_URL_SETTING = "test_captive_portal_https_url"
-private const val TEST_CAPTIVE_PORTAL_HTTP_URL_SETTING = "test_captive_portal_http_url"
-private const val TEST_URL_EXPIRATION_TIME = "test_url_expiration_time"
-
-private const val TEST_HTTPS_URL_PATH = "https_path"
-private const val TEST_HTTP_URL_PATH = "http_path"
-private const val TEST_PORTAL_URL_PATH = "portal_path"
+private const val TEST_HTTPS_URL_PATH = "/https_path"
+private const val TEST_HTTP_URL_PATH = "/http_path"
+private const val TEST_PORTAL_URL_PATH = "/portal_path"
 
 private const val LOCALHOST_HOSTNAME = "localhost"
 
@@ -88,24 +89,24 @@
     private val pm by lazy { context.packageManager }
     private val utils by lazy { CtsNetUtils(context) }
 
-    private val server = HttpServer()
+    private val server = TestHttpServer("localhost")
 
     @Before
     fun setUp() {
-        doAsShell(READ_DEVICE_CONFIG) {
+        runAsShell(READ_DEVICE_CONFIG) {
             // Verify that the test URLs are not normally set on the device, but do not fail if the
             // test URLs are set to what this test uses (URLs on localhost), in case the test was
             // interrupted manually and rerun.
-            assertEmptyOrLocalhostUrl(TEST_CAPTIVE_PORTAL_HTTPS_URL_SETTING)
-            assertEmptyOrLocalhostUrl(TEST_CAPTIVE_PORTAL_HTTP_URL_SETTING)
+            assertEmptyOrLocalhostUrl(TEST_CAPTIVE_PORTAL_HTTPS_URL)
+            assertEmptyOrLocalhostUrl(TEST_CAPTIVE_PORTAL_HTTP_URL)
         }
-        clearTestUrls()
+        clearValidationTestUrlsDeviceConfig()
         server.start()
     }
 
     @After
     fun tearDown() {
-        clearTestUrls()
+        clearValidationTestUrlsDeviceConfig()
         if (pm.hasSystemFeature(FEATURE_WIFI)) {
             reconnectWifi()
         }
@@ -118,12 +119,6 @@
                 "$urlKey must not be set in production scenarios (current value: $url)")
     }
 
-    private fun clearTestUrls() {
-        setHttpsUrl(null)
-        setHttpUrl(null)
-        setUrlExpiration(null)
-    }
-
     @Test
     fun testCaptivePortalIsNotDefaultNetwork() {
         assumeTrue(pm.hasSystemFeature(FEATURE_TELEPHONY))
@@ -132,19 +127,15 @@
         utils.connectToCell()
 
         // Have network validation use a local server that serves a HTTPS error / HTTP redirect
-        server.addResponse(TEST_PORTAL_URL_PATH, Status.OK,
+        server.addResponse(Request(TEST_PORTAL_URL_PATH), Status.OK,
                 content = "Test captive portal content")
-        server.addResponse(TEST_HTTPS_URL_PATH, Status.INTERNAL_ERROR)
-        server.addResponse(TEST_HTTP_URL_PATH, Status.REDIRECT,
-                locationHeader = server.makeUrl(TEST_PORTAL_URL_PATH))
-        setHttpsUrl(server.makeUrl(TEST_HTTPS_URL_PATH))
-        setHttpUrl(server.makeUrl(TEST_HTTP_URL_PATH))
+        server.addResponse(Request(TEST_HTTPS_URL_PATH), Status.INTERNAL_ERROR)
+        server.addResponse(Request(TEST_HTTP_URL_PATH), Status.REDIRECT,
+                locationHeader = makeUrl(TEST_PORTAL_URL_PATH))
+        setHttpsUrlDeviceConfig(makeUrl(TEST_HTTPS_URL_PATH))
+        setHttpUrlDeviceConfig(makeUrl(TEST_HTTP_URL_PATH))
         // URL expiration needs to be in the next 10 minutes
-        setUrlExpiration(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(9))
-
-        // Expect the portal content to be fetched at some point after detecting the portal.
-        // Some implementations may fetch the URL before startCaptivePortalApp is called.
-        val portalContentRequestCv = server.addExpectRequestCv(TEST_PORTAL_URL_PATH)
+        setUrlExpirationDeviceConfig(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(9))
 
         // Wait for a captive portal to be detected on the network
         val wifiNetworkFuture = CompletableFuture<Network>()
@@ -173,9 +164,14 @@
             val startPortalAppPermission =
                     if (isDevSdkInRange(0, 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.")
+            runAsShell(startPortalAppPermission) { cm.startCaptivePortalApp(network) }
+
+            // Expect the portal content to be fetched at some point after detecting the portal.
+            // Some implementations may fetch the URL before startCaptivePortalApp is called.
+            assertNotNull(server.requestsRecord.poll(TEST_TIMEOUT_MS, pos = 0) {
+                it.path == TEST_PORTAL_URL_PATH
+            }, "The captive portal login page was still not fetched ${TEST_TIMEOUT_MS}ms " +
+                    "after startCaptivePortalApp.")
 
             assertNotEquals(network, cm.activeNetwork, wifiDefaultMessage)
         } finally {
@@ -186,73 +182,13 @@
         }
     }
 
-    private fun setHttpsUrl(url: String?) = setConfig(TEST_CAPTIVE_PORTAL_HTTPS_URL_SETTING, url)
-    private fun setHttpUrl(url: String?) = setConfig(TEST_CAPTIVE_PORTAL_HTTP_URL_SETTING, url)
-    private fun setUrlExpiration(timestamp: Long?) = setConfig(TEST_URL_EXPIRATION_TIME,
-            timestamp?.toString())
-
-    private fun setConfig(configKey: String, value: String?) {
-        doAsShell(WRITE_DEVICE_CONFIG) {
-            DeviceConfig.setProperty(
-                    NAMESPACE_CONNECTIVITY, configKey, value, false /* makeDefault */)
-        }
-    }
-
-    private fun doAsShell(vararg permissions: String, action: () -> Unit) {
-        // Wrap the below call to allow for more kotlin-like syntax
-        SystemUtil.runWithShellPermissionIdentity(action, permissions)
-    }
+    /**
+     * Create a URL string that, when fetched, will hit the test server with the given URL [path].
+     */
+    private fun makeUrl(path: String) = "http://localhost:${server.listeningPort}" + path
 
     private fun reconnectWifi() {
         utils.ensureWifiDisconnected(null /* wifiNetworkToCheck */)
         utils.ensureWifiConnected()
     }
-
-    /**
-     * A minimal HTTP server running on localhost (loopback), on a random available port.
-     */
-    private class HttpServer : NanoHTTPD("localhost", 0 /* auto-select the port */) {
-        // Map of URL path -> HTTP response code
-        private val responses = HashMap<String, Response>()
-
-        // Map of path -> CV to open as soon as a request to the path is received
-        private val waitForRequestCv = HashMap<String, ConditionVariable>()
-
-        /**
-         * Create a URL string that, when fetched, will hit this server with the given URL [path].
-         */
-        fun makeUrl(path: String): String {
-            return Uri.Builder()
-                    .scheme("http")
-                    .encodedAuthority("localhost:$listeningPort")
-                    .query(path)
-                    .build()
-                    .toString()
-        }
-
-        fun addResponse(
-            path: String,
-            statusCode: IStatus,
-            locationHeader: String? = null,
-            content: String = ""
-        ) {
-            val response = newFixedLengthResponse(statusCode, "text/plain", content)
-            locationHeader?.let { response.addHeader("Location", it) }
-            responses[path] = response
-        }
-
-        /**
-         * Create a [ConditionVariable] that will open when a request to [path] is received.
-         */
-        fun addExpectRequestCv(path: String): ConditionVariable {
-            return ConditionVariable().apply { waitForRequestCv[path] = this }
-        }
-
-        override fun serve(session: IHTTPSession): Response {
-            waitForRequestCv[session.queryParameterString]?.open()
-            return responses[session.queryParameterString]
-                    // Default response is a 404
-                    ?: super.serve(session)
-        }
-    }
 }
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
index 8f42f79..858a677 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
@@ -77,7 +77,7 @@
 
 import com.android.internal.telephony.uicc.IccUtils;
 import com.android.internal.util.ArrayUtils;
-import com.android.testutils.ArrayTrackRecord;
+import com.android.net.module.util.ArrayTrackRecord;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 import com.android.testutils.DevSdkIgnoreRunner;
 import com.android.testutils.SkipPresubmit;
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 0050318..0790acb 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -204,6 +204,8 @@
             } catch (Exception e) {}
         }
         mUiAutomation = mInstrumentation.getUiAutomation();
+
+        assertNotNull("CTS requires a working Internet connection", mCm.getActiveNetwork());
     }
 
     @After
@@ -352,8 +354,8 @@
                 wifiAddressString, wifiNetwork, cellNetwork),
                 wifiAddressString.equals(cellAddressString));
 
-        // Sanity check that the IP addresses that the requests appeared to come from
-        // are actually on the respective networks.
+        // Verify that the IP addresses that the requests appeared to come from are actually on the
+        // respective networks.
         assertOnNetwork(wifiAddressString, wifiNetwork);
         assertOnNetwork(cellAddressString, cellNetwork);
 
@@ -709,7 +711,7 @@
 
     private void assertMultipathPreferenceIsEventually(Network network, int oldValue,
             int expectedValue) {
-        // Sanity check : if oldValue == expectedValue, there is no way to guarantee the test
+        // Quick check : if oldValue == expectedValue, there is no way to guarantee the test
         // is not flaky.
         assertNotSame(oldValue, expectedValue);
 
@@ -1013,7 +1015,7 @@
         // 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.
+            // Verify that the callback result is expected.
             runWithShellPermissionIdentity(() -> {
                 assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 0, 1));
             });
diff --git a/tests/cts/net/src/android/net/cts/IpConfigurationTest.java b/tests/cts/net/src/android/net/cts/IpConfigurationTest.java
index c6bc077..56ab2a7 100644
--- a/tests/cts/net/src/android/net/cts/IpConfigurationTest.java
+++ b/tests/cts/net/src/android/net/cts/IpConfigurationTest.java
@@ -16,7 +16,7 @@
 
 package android.net.cts;
 
-import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+import static com.android.testutils.ParcelUtils.assertParcelSane;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
diff --git a/tests/cts/net/src/android/net/cts/MacAddressTest.java b/tests/cts/net/src/android/net/cts/MacAddressTest.java
index 4d25e62..3fd3bba 100644
--- a/tests/cts/net/src/android/net/cts/MacAddressTest.java
+++ b/tests/cts/net/src/android/net/cts/MacAddressTest.java
@@ -20,7 +20,7 @@
 import static android.net.MacAddress.TYPE_MULTICAST;
 import static android.net.MacAddress.TYPE_UNICAST;
 
-import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+import static com.android.testutils.ParcelUtils.assertParcelSane;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertFalse;
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index c8e1fc3..d2ca3f8 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -39,13 +39,6 @@
 import android.net.SocketKeepalive
 import android.net.StringNetworkSpecifier
 import android.net.Uri
-import android.os.Build
-import android.os.Bundle
-import android.os.Handler
-import android.os.HandlerThread
-import android.os.Looper
-import android.os.Message
-import android.os.Messenger
 import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnAddKeepalivePacketFilter
 import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnAutomaticReconnectDisabled
 import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnBandwidthUpdateRequested
@@ -56,15 +49,21 @@
 import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnStartSocketKeepalive
 import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnStopSocketKeepalive
 import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnValidationStatus
+import android.os.Build
+import android.os.Bundle
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
+import android.os.Message
+import android.os.Messenger
 import androidx.test.InstrumentationRegistry
 import androidx.test.runner.AndroidJUnit4
 import com.android.internal.util.AsyncChannel
-import com.android.testutils.ArrayTrackRecord
+import com.android.net.module.util.ArrayTrackRecord
 import com.android.testutils.DevSdkIgnoreRule
 import com.android.testutils.RecorderCallback.CallbackEntry.Available
 import com.android.testutils.RecorderCallback.CallbackEntry.Lost
 import com.android.testutils.TestableNetworkCallback
-import java.util.UUID
 import org.junit.After
 import org.junit.Assert.assertArrayEquals
 import org.junit.Assert.fail
@@ -74,9 +73,10 @@
 import org.junit.runner.RunWith
 import java.net.InetAddress
 import java.time.Duration
+import java.util.UUID
 import kotlin.test.assertEquals
-import kotlin.test.assertFalse
 import kotlin.test.assertFailsWith
+import kotlin.test.assertFalse
 import kotlin.test.assertNotNull
 import kotlin.test.assertNull
 import kotlin.test.assertTrue
diff --git a/tests/cts/net/src/android/net/cts/NetworkStackDependenciesTest.kt b/tests/cts/net/src/android/net/cts/NetworkStackDependenciesTest.kt
new file mode 100644
index 0000000..1a7f955
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/NetworkStackDependenciesTest.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts
+
+import android.content.pm.PackageManager
+import android.net.cts.util.CtsNetUtils
+import android.net.wifi.WifiManager
+import android.os.Build
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assume.assumeTrue
+import org.junit.Test
+import kotlin.test.assertNotNull
+import kotlin.test.assertTrue
+
+/**
+ * Basic tests for APIs used by the network stack module.
+ */
+class NetworkStackDependenciesTest {
+    @Test
+    @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.Q)
+    fun testGetFrequency() {
+        // WifiInfo#getFrequency was missing a CTS test in Q: this test is run as part of MTS on Q
+        // devices to ensure it behaves correctly.
+        val context = InstrumentationRegistry.getInstrumentation().getContext()
+        assumeTrue("This test only applies to devices that support wifi",
+                context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI))
+        val wifiManager = context.getSystemService(WifiManager::class.java)
+        assertNotNull(wifiManager, "Device supports wifi but there is no WifiManager")
+
+        CtsNetUtils(context).ensureWifiConnected()
+        val wifiInfo = wifiManager.getConnectionInfo()
+        // The NetworkStack can handle any value of getFrequency; unknown frequencies will not be
+        // classified in metrics, but this is expected behavior. It is only important that the
+        // method does not crash. Still verify that the frequency is positive
+        val frequency = wifiInfo.getFrequency()
+        assertTrue(frequency > 0, "Frequency must be > 0")
+    }
+}
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/cts/CaptivePortalApiTest.kt b/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt
similarity index 82%
rename from tests/cts/net/src/android/net/cts/CaptivePortalApiTest.kt
rename to tests/cts/net/src/android/net/cts/NetworkValidationTest.kt
index ef2b0ce..ec656de 100644
--- a/tests/cts/net/src/android/net/cts/CaptivePortalApiTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt
@@ -19,16 +19,19 @@
 import android.Manifest.permission.MANAGE_TEST_NETWORKS
 import android.Manifest.permission.NETWORK_SETTINGS
 import android.content.Context
+import android.content.pm.PackageManager
 import android.net.ConnectivityManager
 import android.net.EthernetManager
 import android.net.InetAddresses
 import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
 import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
 import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
+import android.net.NetworkCapabilities.TRANSPORT_TEST
 import android.net.NetworkRequest
 import android.net.TestNetworkInterface
 import android.net.TestNetworkManager
 import android.net.Uri
+import android.net.cts.NetworkValidationTestUtil.runAsShell
 import android.net.dhcp.DhcpDiscoverPacket
 import android.net.dhcp.DhcpPacket
 import android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE
@@ -40,27 +43,26 @@
 import android.platform.test.annotations.AppModeFull
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.runner.AndroidJUnit4
-import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
-import com.android.compatibility.common.util.ThrowingRunnable
 import com.android.net.module.util.Inet4AddressUtils.getBroadcastAddress
 import com.android.net.module.util.Inet4AddressUtils.getPrefixMaskAsInet4Address
 import com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY
+import com.android.testutils.ArpResponder
 import com.android.testutils.DevSdkIgnoreRule
 import com.android.testutils.DhcpClientPacketFilter
 import com.android.testutils.DhcpOptionFilter
 import com.android.testutils.RecorderCallback.CallbackEntry
 import com.android.testutils.TapPacketReader
+import com.android.testutils.TestHttpServer
 import com.android.testutils.TestableNetworkCallback
-import fi.iki.elonen.NanoHTTPD
+import fi.iki.elonen.NanoHTTPD.Response.Status
 import org.junit.After
 import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import java.net.Inet4Address
-import java.util.concurrent.ArrayBlockingQueue
-import java.util.concurrent.TimeUnit
 import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
 import kotlin.test.assertTrue
@@ -79,7 +81,7 @@
 
 @AppModeFull(reason = "Instant apps cannot create test networks")
 @RunWith(AndroidJUnit4::class)
-class CaptivePortalApiTest {
+class NetworkValidationTest {
     @JvmField
     @Rule
     val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = Build.VERSION_CODES.Q)
@@ -89,14 +91,15 @@
     private val eth by lazy { context.assertHasService(EthernetManager::class.java) }
     private val cm by lazy { context.assertHasService(ConnectivityManager::class.java) }
 
-    private val handlerThread = HandlerThread(CaptivePortalApiTest::class.java.simpleName)
+    private val handlerThread = HandlerThread(NetworkValidationTest::class.java.simpleName)
     private val serverIpAddr = InetAddresses.parseNumericAddress("192.0.2.222") as Inet4Address
     private val clientIpAddr = InetAddresses.parseNumericAddress("192.0.2.111") as Inet4Address
-    private val httpServer = HttpServer()
+    private val httpServer = TestHttpServer()
     private val ethRequest = NetworkRequest.Builder()
             // ETHERNET|TEST transport networks do not have NET_CAPABILITY_TRUSTED
             .removeCapability(NET_CAPABILITY_TRUSTED)
-            .addTransportType(TRANSPORT_ETHERNET).build()
+            .addTransportType(TRANSPORT_ETHERNET)
+            .addTransportType(TRANSPORT_TEST).build()
     private val ethRequestCb = TestableNetworkCallback()
 
     private lateinit var iface: TestNetworkInterface
@@ -107,9 +110,10 @@
 
     @Before
     fun setUp() {
-        // This test requires using a tap interface as the default ethernet interface: skip if there
-        // is already an ethernet interface connected.
-        testSkipped = eth.isAvailable()
+        // This test requires using a tap interface as an ethernet interface.
+        val pm = context.getPackageManager()
+        testSkipped = !pm.hasSystemFeature(PackageManager.FEATURE_ETHERNET) &&
+                context.getSystemService(EthernetManager::class.java) == null
         assumeFalse(testSkipped)
 
         // Register a request so the network does not get torn down
@@ -151,7 +155,15 @@
     }
 
     @Test
-    fun testApiCallbacks() {
+    fun testCapportApiCallbacks() {
+        httpServer.addResponse(capportUrl, Status.OK, content = """
+                |{
+                |  "captive": true,
+                |  "user-portal-url": "$TEST_LOGIN_URL",
+                |  "venue-info-url": "$TEST_VENUE_INFO_URL"
+                |}
+            """.trimMargin())
+
         // Handle the DHCP handshake that includes the capport API URL
         val discover = reader.assertDhcpPacketReceived(
                 DhcpDiscoverPacket::class.java, TEST_TIMEOUT_MS, DHCP_MESSAGE_TYPE_DISCOVER)
@@ -163,11 +175,9 @@
         assertEquals(clientIpAddr, request.mRequestedIp)
         reader.sendResponse(makeAckPacket(request.clientMac, request.transactionId))
 
-        // Expect a request to the capport API
-        val capportReq = httpServer.recordedRequests.poll(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
-        assertNotNull(capportReq, "The device did not fetch captive portal API data within timeout")
-        assertEquals(capportUrl.path, capportReq.uri)
-        assertEquals(capportUrl.query, capportReq.queryParameterString)
+        // The first request received by the server should be for the portal API
+        assertTrue(httpServer.requestsRecord.poll(TEST_TIMEOUT_MS, 0)?.matches(capportUrl) ?: false,
+                "The device did not fetch captive portal API data within timeout")
 
         // Expect network callbacks with capport info
         val testCb = TestableNetworkCallback(TEST_TIMEOUT_MS)
@@ -216,30 +226,6 @@
                     listOf(serverIpAddr) /* gateways */, listOf(serverIpAddr) /* dnsServers */,
                     serverIpAddr, TEST_DOMAIN_NAME, null /* hostname */, true /* metered */,
                     TEST_MTU, false /* rapidCommit */, capportUrl.toString())
-
-    private fun parseDhcpPacket(bytes: ByteArray) = DhcpPacket.decodeFullPacket(
-            bytes, MAX_PACKET_LENGTH, DhcpPacket.ENCAP_L2)
-}
-
-/**
- * A minimal HTTP server running on localhost (loopback), on a random available port.
- *
- * The server records each request in [recordedRequests] and will not serve any further request
- * until the last one is removed from the queue for verification.
- */
-private class HttpServer : NanoHTTPD("localhost", 0 /* auto-select the port */) {
-    val recordedRequests = ArrayBlockingQueue<IHTTPSession>(1 /* capacity */)
-
-    override fun serve(session: IHTTPSession): Response {
-        recordedRequests.offer(session)
-        return newFixedLengthResponse("""
-                |{
-                |  "captive": true,
-                |  "user-portal-url": "$TEST_LOGIN_URL",
-                |  "venue-info-url": "$TEST_VENUE_INFO_URL"
-                |}
-            """.trimMargin())
-    }
 }
 
 private fun <T : DhcpPacket> TapPacketReader.assertDhcpPacketReceived(
@@ -259,12 +245,3 @@
 private fun <T> Context.assertHasService(manager: Class<T>): T {
     return getSystemService(manager) ?: fail("Service $manager not found")
 }
-
-/**
- * Wrapper around runWithShellPermissionIdentity with kotlin-like syntax.
- */
-private fun <T> runAsShell(vararg permissions: String, task: () -> T): T {
-    var ret: T? = null
-    runWithShellPermissionIdentity(ThrowingRunnable { ret = task() }, *permissions)
-    return ret ?: fail("ThrowingRunnable was not run")
-}
diff --git a/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt b/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
new file mode 100644
index 0000000..5ef1854
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts
+
+import android.Manifest
+import android.net.util.NetworkStackUtils
+import android.provider.DeviceConfig
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.compatibility.common.util.ThrowingRunnable
+import kotlin.test.fail
+
+/**
+ * Collection of utility methods for configuring network validation.
+ */
+internal object NetworkValidationTestUtil {
+
+    /**
+     * Clear the test network validation URLs.
+     */
+    fun clearValidationTestUrlsDeviceConfig() {
+        setHttpsUrlDeviceConfig(null)
+        setHttpUrlDeviceConfig(null)
+        setUrlExpirationDeviceConfig(null)
+    }
+
+    /**
+     * Set the test validation HTTPS URL.
+     *
+     * @see NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL
+     */
+    fun setHttpsUrlDeviceConfig(url: String?) =
+            setConfig(NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL, url)
+
+    /**
+     * Set the test validation HTTP URL.
+     *
+     * @see NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL
+     */
+    fun setHttpUrlDeviceConfig(url: String?) =
+            setConfig(NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL, url)
+
+    /**
+     * Set the test validation URL expiration.
+     *
+     * @see NetworkStackUtils.TEST_URL_EXPIRATION_TIME
+     */
+    fun setUrlExpirationDeviceConfig(timestamp: Long?) =
+            setConfig(NetworkStackUtils.TEST_URL_EXPIRATION_TIME, timestamp?.toString())
+
+    private fun setConfig(configKey: String, value: String?) {
+        runAsShell(Manifest.permission.WRITE_DEVICE_CONFIG) {
+            DeviceConfig.setProperty(
+                    DeviceConfig.NAMESPACE_CONNECTIVITY, configKey, value, false /* makeDefault */)
+        }
+    }
+
+    /**
+     * Wrapper around runWithShellPermissionIdentity with kotlin-like syntax.
+     */
+    fun <T> runAsShell(vararg permissions: String, task: () -> T): T {
+        var ret: T? = null
+        runWithShellPermissionIdentity(ThrowingRunnable { ret = task() }, *permissions)
+        return ret ?: fail("ThrowingRunnable did not return")
+    }
+}
\ No newline at end of file
diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
index f47f454..65a8c7c 100644
--- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -72,7 +72,7 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.testutils.ArrayTrackRecord;
+import com.android.net.module.util.ArrayTrackRecord;
 
 import org.junit.After;
 import org.junit.Before;