From 10980d136ce7184bcee0c6c755c1350292e04608 Mon Sep 17 00:00:00 2001 From: Remi NGUYEN VAN Date: Thu, 4 Jun 2020 15:56:45 +0900 Subject: [PATCH] Auto-configure wifi on virtual devices At the moment tests run on virtual devices through TEST_MAPPING do not necessarily have wifi configured to auto-connect to a test SSID. This could be fixed by adding a TargetPreparer in AndroidTest.xml with a given SSID, but that configuration would then apply to all runs, including local ones. There is also no good way to apply a preparer that configures wifi "if it is not already connected": WifiPreparer in particular will not configure wifi even if just a mobile network is active. Implement auto-configuration in CtsNetUtils#connectToWifi so that if no network is configured, and a virtual SSID is detected when scanning, that SSID will be added to the list of configured networks. This allows addressing the issue until TEST_MAPPING can support configuring wifi, with minimal side-effects on other runs. Test: atest CtsNetTestCasesLatestSdk:CaptivePortalTest Bug: 158153057 Change-Id: I47df4c325b073f8a9bf320894245eed211606952 --- .../android/net/cts/util/CtsNetUtils.java | 107 ++++++++++++++++-- 1 file changed, 97 insertions(+), 10 deletions(-) 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 85d2113d89..f1bc130f2c 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 @@ -21,6 +21,9 @@ import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_TEST; +import static android.net.wifi.WifiManager.SCAN_RESULTS_AVAILABLE_ACTION; + +import static com.android.compatibility.common.util.ShellIdentityUtils.invokeWithShellPermissions; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -44,6 +47,7 @@ import android.net.NetworkInfo; import android.net.NetworkInfo.State; import android.net.NetworkRequest; import android.net.TestNetworkManager; +import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; @@ -58,13 +62,21 @@ import android.util.Log; import com.android.compatibility.common.util.SystemUtil; +import junit.framework.AssertionFailedError; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public final class CtsNetUtils { private static final String TAG = CtsNetUtils.class.getSimpleName(); @@ -72,7 +84,8 @@ public final class CtsNetUtils { private static final int SOCKET_TIMEOUT_MS = 2000; private static final int PRIVATE_DNS_PROBE_MS = 1_000; - public static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 6_000; + private static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 6_000; + private static final int CONNECTIVITY_CHANGE_TIMEOUT_SECS = 30; public static final int HTTP_PORT = 80; public static final String TEST_HOST = "connectivitycheck.gstatic.com"; public static final String HTTP_REQUEST = @@ -170,8 +183,8 @@ public final class CtsNetUtils { /** * Enable WiFi and wait for it to become connected to a network. * - * A network is considered connected when a {@link NetworkCallback#onAvailable(Network)} - * callback is received. + * A network is considered connected when a {@link NetworkRequest} with TRANSPORT_WIFI + * receives a {@link NetworkCallback#onAvailable(Network)} callback. */ public Network ensureWifiConnected() { return connectToWifi(false /* expectLegacyBroadcast */); @@ -202,8 +215,18 @@ public final class CtsNetUtils { try { clearWifiBlacklist(); SystemUtil.runShellCommand("svc wifi enable"); - SystemUtil.runWithShellPermissionIdentity(() -> mWifiManager.reconnect(), - NETWORK_SETTINGS); + final WifiConfiguration config = maybeAddVirtualWifiConfiguration(); + if (config == null) { + // TODO: this may not clear the BSSID blacklist, as opposed to + // mWifiManager.connect(config) + SystemUtil.runWithShellPermissionIdentity(() -> mWifiManager.reconnect(), + NETWORK_SETTINGS); + } else { + // When running CTS, devices are expected to have wifi networks pre-configured. + // This condition is only hit on virtual devices. + SystemUtil.runWithShellPermissionIdentity( + () -> mWifiManager.connect(config, null /* listener */), NETWORK_SETTINGS); + } // Ensure we get an onAvailable callback and possibly a CONNECTIVITY_ACTION. wifiNetwork = callback.waitForAvailable(); assertNotNull(err, wifiNetwork); @@ -219,6 +242,68 @@ public final class CtsNetUtils { return wifiNetwork; } + private WifiConfiguration maybeAddVirtualWifiConfiguration() { + final List configs = invokeWithShellPermissions( + mWifiManager::getConfiguredNetworks); + // If no network is configured, add a config for virtual access points if applicable + if (configs.size() == 0) { + final List scanResults = getWifiScanResults(); + final WifiConfiguration virtualConfig = maybeConfigureVirtualNetwork(scanResults); + assertNotNull("The device has no configured wifi network", virtualConfig); + + return virtualConfig; + } + // No need to add a configuration: there is already one + return null; + } + + private List getWifiScanResults() { + final CompletableFuture> scanResultsFuture = new CompletableFuture<>(); + SystemUtil.runWithShellPermissionIdentity(() -> { + final BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + scanResultsFuture.complete(mWifiManager.getScanResults()); + } + }; + mContext.registerReceiver(receiver, new IntentFilter(SCAN_RESULTS_AVAILABLE_ACTION)); + mWifiManager.startScan(); + }); + + try { + return scanResultsFuture.get(CONNECTIVITY_CHANGE_TIMEOUT_SECS, TimeUnit.SECONDS); + } catch (ExecutionException | InterruptedException | TimeoutException e) { + throw new AssertionFailedError("Wifi scan results not received within timeout"); + } + } + + /** + * If a virtual wifi network is detected, add a configuration for that network. + * TODO(b/158150376): have the test infrastructure add virtual wifi networks when appropriate. + */ + private WifiConfiguration maybeConfigureVirtualNetwork(List scanResults) { + // Virtual wifi networks used on the emulator and cloud testing infrastructure + final List virtualSsids = Arrays.asList("VirtWifi", "AndroidWifi"); + Log.d(TAG, "Wifi scan results: " + scanResults); + final ScanResult virtualScanResult = scanResults.stream().filter( + s -> virtualSsids.contains(s.SSID)).findFirst().orElse(null); + + // Only add the virtual configuration if the virtual AP is detected in scans + if (virtualScanResult == null) return null; + + final WifiConfiguration virtualConfig = new WifiConfiguration(); + // ASCII SSIDs need to be surrounded by double quotes + virtualConfig.SSID = "\"" + virtualScanResult.SSID + "\""; + virtualConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + + SystemUtil.runWithShellPermissionIdentity(() -> { + final int networkId = mWifiManager.addNetwork(virtualConfig); + assertTrue(networkId >= 0); + assertTrue(mWifiManager.enableNetwork(networkId, false /* attemptConnect */)); + }); + return virtualConfig; + } + /** * 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 @@ -226,8 +311,8 @@ public final class CtsNetUtils { */ private void clearWifiBlacklist() { SystemUtil.runWithShellPermissionIdentity(() -> { - for (WifiConfiguration config : mWifiManager.getConfiguredNetworks()) { - mWifiManager.enableNetwork(config.networkId, false /* attemptConnect */); + for (WifiConfiguration cfg : mWifiManager.getConfiguredNetworks()) { + assertTrue(mWifiManager.enableNetwork(cfg.networkId, false /* attemptConnect */)); } }); } @@ -533,7 +618,7 @@ public final class CtsNetUtils { } public boolean waitForState() throws InterruptedException { - return mReceiveLatch.await(30, TimeUnit.SECONDS); + return mReceiveLatch.await(CONNECTIVITY_CHANGE_TIMEOUT_SECS, TimeUnit.SECONDS); } } @@ -550,11 +635,13 @@ public final class CtsNetUtils { public Network lastLostNetwork; public Network waitForAvailable() throws InterruptedException { - return mAvailableLatch.await(30, TimeUnit.SECONDS) ? currentNetwork : null; + return mAvailableLatch.await(CONNECTIVITY_CHANGE_TIMEOUT_SECS, TimeUnit.SECONDS) + ? currentNetwork : null; } public Network waitForLost() throws InterruptedException { - return mLostLatch.await(30, TimeUnit.SECONDS) ? lastLostNetwork : null; + return mLostLatch.await(CONNECTIVITY_CHANGE_TIMEOUT_SECS, TimeUnit.SECONDS) + ? lastLostNetwork : null; } public boolean waitForUnavailable() throws InterruptedException {