Merge "Enlarge the onAvailable callback timeout to 30 seconds"
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index 82b7413..052ab26 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -82,6 +82,7 @@
     min_sdk_version: "29",
     target_sdk_version: "29",
     test_suites: [
+        "device-tests",
         "mts",
     ],
     test_config_template: "AndroidTestTemplate.xml",
diff --git a/tests/cts/net/TEST_MAPPING b/tests/cts/net/TEST_MAPPING
new file mode 100644
index 0000000..e2a9c75
--- /dev/null
+++ b/tests/cts/net/TEST_MAPPING
@@ -0,0 +1,13 @@
+{
+  // TODO: move to mainline-presubmit once supported
+  "postsubmit": [
+    {
+      "name": "CtsNetTestCasesLatestSdk",
+      "options": [
+        {
+          "exclude-annotation": "com.android.testutils.SkipPresubmit"
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/cts/net/api23Test/AndroidManifest.xml b/tests/cts/net/api23Test/AndroidManifest.xml
index 8af87f6..4889660 100644
--- a/tests/cts/net/api23Test/AndroidManifest.xml
+++ b/tests/cts/net/api23Test/AndroidManifest.xml
@@ -18,8 +18,11 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="android.net.cts.api23test">
 
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <uses-permission android:name="android.permission.INTERNET" />
 
     <application android:usesCleartextTraffic="true">
diff --git a/tests/cts/net/src/android/net/cts/CaptivePortalApiTest.kt b/tests/cts/net/src/android/net/cts/CaptivePortalApiTest.kt
index 40d0ca6..68d5281 100644
--- a/tests/cts/net/src/android/net/cts/CaptivePortalApiTest.kt
+++ b/tests/cts/net/src/android/net/cts/CaptivePortalApiTest.kt
@@ -61,7 +61,6 @@
 import java.net.Inet4Address
 import java.util.concurrent.ArrayBlockingQueue
 import java.util.concurrent.TimeUnit
-import kotlin.reflect.KClass
 import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
 import kotlin.test.assertTrue
@@ -90,7 +89,7 @@
     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.simpleName)
+    private val handlerThread = HandlerThread(CaptivePortalApiTest::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()
@@ -155,11 +154,11 @@
     fun testApiCallbacks() {
         // Handle the DHCP handshake that includes the capport API URL
         val discover = reader.assertDhcpPacketReceived(
-                DhcpDiscoverPacket::class, TEST_TIMEOUT_MS, DHCP_MESSAGE_TYPE_DISCOVER)
+                DhcpDiscoverPacket::class.java, TEST_TIMEOUT_MS, DHCP_MESSAGE_TYPE_DISCOVER)
         reader.sendResponse(makeOfferPacket(discover.clientMac, discover.transactionId))
 
         val request = reader.assertDhcpPacketReceived(
-                DhcpRequestPacket::class, TEST_TIMEOUT_MS, DHCP_MESSAGE_TYPE_REQUEST)
+                DhcpRequestPacket::class.java, TEST_TIMEOUT_MS, DHCP_MESSAGE_TYPE_REQUEST)
         assertEquals(discover.transactionId, request.transactionId)
         assertEquals(clientIpAddr, request.mRequestedIp)
         reader.sendResponse(makeAckPacket(request.clientMac, request.transactionId))
@@ -244,7 +243,7 @@
 }
 
 private fun <T : DhcpPacket> TapPacketReader.assertDhcpPacketReceived(
-    packetType: KClass<T>,
+    packetType: Class<T>,
     timeoutMs: Long,
     type: Byte
 ): T {
@@ -254,7 +253,7 @@
     val packet = DhcpPacket.decodeFullPacket(packetBytes, packetBytes.size, DhcpPacket.ENCAP_L2)
     assertTrue(packetType.isInstance(packet),
             "Expected ${packetType.simpleName} but got ${packet.javaClass.simpleName}")
-    return packetType.java.cast(packet)
+    return packetType.cast(packet)
 }
 
 private fun <T> Context.assertHasService(manager: Class<T>): T {
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index d498ed9..3880664 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -96,6 +96,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.testutils.SkipPresubmit;
 
 import libcore.io.Streams;
 
@@ -325,6 +326,7 @@
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
     @Test
+    @SkipPresubmit(reason = "Virtual devices use a single internet connection for all networks")
     public void testOpenConnection() throws Exception {
         boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
                 && mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
@@ -990,6 +992,7 @@
 
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
     @Test
+    @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
     public void testCreateTcpKeepalive() throws Exception {
         if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testCreateTcpKeepalive cannot execute unless device supports WiFi");
@@ -1199,6 +1202,7 @@
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
     @Test
+    @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
     public void testSocketKeepaliveLimitWifi() throws Exception {
         if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testSocketKeepaliveLimitWifi cannot execute unless device"
@@ -1252,6 +1256,7 @@
      */
     @AppModeFull(reason = "Cannot request network in instant app mode")
     @Test
+    @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
     public void testSocketKeepaliveLimitTelephony() throws Exception {
         if (!mPackageManager.hasSystemFeature(FEATURE_TELEPHONY)) {
             Log.i(TAG, "testSocketKeepaliveLimitTelephony cannot execute unless device"
@@ -1294,6 +1299,7 @@
      */
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
     @Test
+    @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
     public void testSocketKeepaliveUnprivileged() throws Exception {
         if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
             Log.i(TAG, "testSocketKeepaliveUnprivileged cannot execute unless device"
diff --git a/tests/cts/net/src/android/net/cts/DnsTest.java b/tests/cts/net/src/android/net/cts/DnsTest.java
index 746dcb0..fde27e9 100644
--- a/tests/cts/net/src/android/net/cts/DnsTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsTest.java
@@ -27,6 +27,8 @@
 import android.test.AndroidTestCase;
 import android.util.Log;
 
+import com.android.testutils.SkipPresubmit;
+
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
@@ -68,6 +70,7 @@
      * Perf - measure size of first and second tier caches and their effect
      * Assert requires network permission
      */
+    @SkipPresubmit(reason = "IPv6 support may be missing on presubmit virtual hardware")
     public void testDnsWorks() throws Exception {
         ensureIpv6Connectivity();
 
diff --git a/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java b/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
index 5cc0cb4..dff2581 100644
--- a/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
+++ b/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
@@ -46,13 +46,15 @@
 import android.net.TestNetworkManager;
 import android.net.VpnManager;
 import android.net.cts.util.CtsNetUtils;
+import android.os.Build;
 import android.platform.test.annotations.AppModeFull;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.HexDump;
 import com.android.org.bouncycastle.x509.X509V1CertificateGenerator;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.DevSdkIgnoreRunner;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -70,7 +72,8 @@
 
 import javax.security.auth.x500.X500Principal;
 
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@IgnoreUpTo(Build.VERSION_CODES.Q)
 @AppModeFull(reason = "Appops state changes disallowed for instant apps (OP_ACTIVATE_PLATFORM_VPN)")
 public class Ikev2VpnTest {
     private static final String TAG = Ikev2VpnTest.class.getSimpleName();
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 32cdf92..b1f3602 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
@@ -30,8 +30,8 @@
 import android.annotation.NonNull;
 import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
-import android.content.Context;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
@@ -44,6 +44,8 @@
 import android.net.NetworkInfo.State;
 import android.net.NetworkRequest;
 import android.net.TestNetworkManager;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.Binder;
 import android.os.Build;
@@ -169,6 +171,7 @@
 
         boolean connected = false;
         try {
+            clearWifiBlacklist();
             SystemUtil.runShellCommand("svc wifi enable");
             SystemUtil.runWithShellPermissionIdentity(() -> mWifiManager.reconnect(),
                     NETWORK_SETTINGS);
@@ -188,11 +191,23 @@
         return wifiNetwork;
     }
 
+    /**
+     * Re-enable wifi networks that were blacklisted, typically because no internet connection was
+     * detected the last time they were connected. This is necessary to make sure wifi can reconnect
+     * to them.
+     */
+    private void clearWifiBlacklist() {
+        SystemUtil.runWithShellPermissionIdentity(() -> {
+            for (WifiConfiguration config : mWifiManager.getConfiguredNetworks()) {
+                mWifiManager.enableNetwork(config.networkId, false /* attemptConnect */);
+            }
+        });
+    }
+
     /** Disable WiFi and wait for it to become disconnected from the network. */
     public void disconnectFromWifi(Network wifiNetworkToCheck) {
         final TestNetworkCallback callback = new TestNetworkCallback();
         mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
-        Network lostWifiNetwork = null;
 
         ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
                 mCm, ConnectivityManager.TYPE_WIFI, NetworkInfo.State.DISCONNECTED);
@@ -200,9 +215,15 @@
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         mContext.registerReceiver(receiver, filter);
 
+        final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+        final boolean wasWifiConnected = wifiInfo != null && wifiInfo.getNetworkId() != -1;
         // Assert that we can establish a TCP connection on wifi.
         Socket wifiBoundSocket = null;
         if (wifiNetworkToCheck != null) {
+            assertTrue("Cannot check network " + wifiNetworkToCheck + ": wifi is not connected",
+                    wasWifiConnected);
+            final NetworkCapabilities nc = mCm.getNetworkCapabilities(wifiNetworkToCheck);
+            assertNotNull("Network " + wifiNetworkToCheck + " is not connected", nc);
             try {
                 wifiBoundSocket = getBoundSocket(wifiNetworkToCheck, TEST_HOST, HTTP_PORT);
                 testHttpRequest(wifiBoundSocket);
@@ -211,13 +232,14 @@
             }
         }
 
-        boolean disconnected = false;
         try {
             SystemUtil.runShellCommand("svc wifi disable");
-            // Ensure we get both an onLost callback and a CONNECTIVITY_ACTION.
-            lostWifiNetwork = callback.waitForLost();
-            assertNotNull(lostWifiNetwork);
-            disconnected = receiver.waitForState();
+            if (wasWifiConnected) {
+                // Ensure we get both an onLost callback and a CONNECTIVITY_ACTION.
+                assertNotNull("Did not receive onLost callback after disabling wifi",
+                        callback.waitForLost());
+                assertTrue("Wifi failed to reach DISCONNECTED state.", receiver.waitForState());
+            }
         } catch (InterruptedException ex) {
             fail("disconnectFromWifi was interrupted");
         } finally {
@@ -225,8 +247,6 @@
             mContext.unregisterReceiver(receiver);
         }
 
-        assertTrue("Wifi failed to reach DISCONNECTED state.", disconnected);
-
         // Check that the socket is closed when wifi disconnects.
         if (wifiBoundSocket != null) {
             try {