| /* |
| * 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.wifi.cts; |
| |
| import static android.os.Process.myUid; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.common.truth.Truth.assertWithMessage; |
| |
| import static org.junit.Assume.assumeTrue; |
| |
| import android.annotation.NonNull; |
| import android.content.Context; |
| import android.net.ConnectivityManager; |
| import android.net.ConnectivityManager.NetworkCallback; |
| import android.net.MacAddress; |
| import android.net.Network; |
| import android.net.NetworkCapabilities; |
| import android.net.NetworkRequest; |
| import android.net.NetworkSpecifier; |
| import android.net.wifi.ScanResult; |
| import android.net.wifi.WifiConfiguration; |
| import android.net.wifi.WifiEnterpriseConfig; |
| import android.net.wifi.WifiInfo; |
| import android.net.wifi.WifiManager; |
| import android.net.wifi.WifiNetworkSpecifier; |
| import android.os.Build; |
| import android.os.PatternMatcher; |
| import android.platform.test.annotations.AppModeFull; |
| import android.support.test.uiautomator.UiDevice; |
| import android.util.Pair; |
| |
| import androidx.test.ext.junit.runners.AndroidJUnit4; |
| import androidx.test.filters.LargeTest; |
| import androidx.test.filters.SdkSuppress; |
| import androidx.test.platform.app.InstrumentationRegistry; |
| |
| import com.android.compatibility.common.util.ApiLevelUtil; |
| import com.android.compatibility.common.util.PollingCheck; |
| import com.android.compatibility.common.util.ShellIdentityUtils; |
| |
| import org.junit.After; |
| import org.junit.AfterClass; |
| import org.junit.Before; |
| import org.junit.BeforeClass; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.InputStream; |
| import java.nio.charset.StandardCharsets; |
| import java.security.KeyFactory; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.PrivateKey; |
| import java.security.cert.CertificateFactory; |
| import java.security.cert.X509Certificate; |
| import java.security.spec.InvalidKeySpecException; |
| import java.security.spec.PKCS8EncodedKeySpec; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.concurrent.LinkedBlockingQueue; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * Tests the entire connection flow using {@link WifiNetworkSpecifier} embedded in a |
| * {@link NetworkRequest} & passed into {@link ConnectivityManager#requestNetwork(NetworkRequest, |
| * ConnectivityManager.NetworkCallback)}. |
| * |
| * Assumes that all the saved networks is either open/WPA1/WPA2/WPA3 authenticated network. |
| */ |
| @AppModeFull(reason = "Cannot get WifiManager in instant app mode") |
| @LargeTest |
| @RunWith(AndroidJUnit4.class) |
| public class WifiNetworkSpecifierTest extends WifiJUnit4TestBase { |
| private static final String TAG = "WifiNetworkSpecifierTest"; |
| |
| private static final String CA_SUITE_B_ECDSA_CERT_STRING = |
| "-----BEGIN CERTIFICATE-----\n" |
| + "MIICTzCCAdSgAwIBAgIUdnLttwNPnQzFufplGOr9bTrGCqMwCgYIKoZIzj0EAwMw\n" |
| + "XjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANNVFYxEDAOBgNV\n" |
| + "BAoMB0FuZHJvaWQxDjAMBgNVBAsMBVdpLUZpMRIwEAYDVQQDDAl1bml0ZXN0Q0Ew\n" |
| + "HhcNMjAwNzIxMDIyNDA1WhcNMzAwNTMwMDIyNDA1WjBeMQswCQYDVQQGEwJVUzEL\n" |
| + "MAkGA1UECAwCQ0ExDDAKBgNVBAcMA01UVjEQMA4GA1UECgwHQW5kcm9pZDEOMAwG\n" |
| + "A1UECwwFV2ktRmkxEjAQBgNVBAMMCXVuaXRlc3RDQTB2MBAGByqGSM49AgEGBSuB\n" |
| + "BAAiA2IABFmntXwk9icqhDQFUP1xy04WyEpaGW4q6Q+8pujlSl/X3iotPZ++GZfp\n" |
| + "Mfv3YDHDBl6sELPQ2BEjyPXmpsKjOUdiUe69e88oGEdeqT2xXiQ6uzpTfJD4170i\n" |
| + "O/TwLrQGKKNTMFEwHQYDVR0OBBYEFCjptsX3g4g5W0L4oEP6N3gfyiZXMB8GA1Ud\n" |
| + "IwQYMBaAFCjptsX3g4g5W0L4oEP6N3gfyiZXMA8GA1UdEwEB/wQFMAMBAf8wCgYI\n" |
| + "KoZIzj0EAwMDaQAwZgIxAK61brUYRbLmQKiaEboZgrHtnPAcGo7Yzx3MwHecx3Dm\n" |
| + "5soIeLVYc8bPYN1pbhXW1gIxALdEe2sh03nBHyQH4adYoZungoCwt8mp/7sJFxou\n" |
| + "9UnRegyBgGzf74ROWdpZHzh+Pg==\n" |
| + "-----END CERTIFICATE-----\n"; |
| public static final X509Certificate CA_SUITE_B_ECDSA_CERT = |
| loadCertificate(CA_SUITE_B_ECDSA_CERT_STRING); |
| |
| private static final String CLIENT_SUITE_B_ECDSA_CERT_STRING = |
| "-----BEGIN CERTIFICATE-----\n" |
| + "MIIB9zCCAX4CFDpfSZh3AH07BEfGWuMDa7Ynz6y+MAoGCCqGSM49BAMDMF4xCzAJ\n" |
| + "BgNVBAYTAlVTMQswCQYDVQQIDAJDQTEMMAoGA1UEBwwDTVRWMRAwDgYDVQQKDAdB\n" |
| + "bmRyb2lkMQ4wDAYDVQQLDAVXaS1GaTESMBAGA1UEAwwJdW5pdGVzdENBMB4XDTIw\n" |
| + "MDcyMTAyMjk1MFoXDTMwMDUzMDAyMjk1MFowYjELMAkGA1UEBhMCVVMxCzAJBgNV\n" |
| + "BAgMAkNBMQwwCgYDVQQHDANNVFYxEDAOBgNVBAoMB0FuZHJvaWQxDjAMBgNVBAsM\n" |
| + "BVdpLUZpMRYwFAYDVQQDDA11bml0ZXN0Q2xpZW50MHYwEAYHKoZIzj0CAQYFK4EE\n" |
| + "ACIDYgAEhxhVJ7dcSqrto0X+dgRxtd8BWG8cWmPjBji3MIxDLfpcMDoIB84ae1Ew\n" |
| + "gJn4YUYHrWsUDiVNihv8j7a/Ol1qcIY2ybH7tbezefLmagqA4vXEUXZXoUyL4ZNC\n" |
| + "DWcdw6LrMAoGCCqGSM49BAMDA2cAMGQCMH4aP73HrriRUJRguiuRic+X4Cqj/7YQ\n" |
| + "ueJmP87KF92/thhoQ9OrRo8uJITPmNDswwIwP2Q1AZCSL4BI9dYrqu07Ar+pSkXE\n" |
| + "R7oOqGdZR+d/MvXcFSrbIaLKEoHXmQamIHLe\n" |
| + "-----END CERTIFICATE-----\n"; |
| public static final X509Certificate CLIENT_SUITE_B_ECDSA_CERT = |
| loadCertificate(CLIENT_SUITE_B_ECDSA_CERT_STRING); |
| |
| private static final byte[] CLIENT_SUITE_B_ECC_KEY_DATA = new byte[]{ |
| (byte) 0x30, (byte) 0x81, (byte) 0xb6, (byte) 0x02, (byte) 0x01, (byte) 0x00, |
| (byte) 0x30, (byte) 0x10, (byte) 0x06, (byte) 0x07, (byte) 0x2a, (byte) 0x86, |
| (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x02, (byte) 0x01, (byte) 0x06, |
| (byte) 0x05, (byte) 0x2b, (byte) 0x81, (byte) 0x04, (byte) 0x00, (byte) 0x22, |
| (byte) 0x04, (byte) 0x81, (byte) 0x9e, (byte) 0x30, (byte) 0x81, (byte) 0x9b, |
| (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x04, (byte) 0x30, (byte) 0xea, |
| (byte) 0x6c, (byte) 0x4b, (byte) 0x6d, (byte) 0x43, (byte) 0xf9, (byte) 0x6c, |
| (byte) 0x91, (byte) 0xdc, (byte) 0x2d, (byte) 0x6e, (byte) 0x87, (byte) 0x4f, |
| (byte) 0x0a, (byte) 0x0b, (byte) 0x97, (byte) 0x25, (byte) 0x1c, (byte) 0x79, |
| (byte) 0xa2, (byte) 0x07, (byte) 0xdc, (byte) 0x94, (byte) 0xc2, (byte) 0xee, |
| (byte) 0x64, (byte) 0x51, (byte) 0x6d, (byte) 0x4e, (byte) 0x35, (byte) 0x1c, |
| (byte) 0x22, (byte) 0x2f, (byte) 0xc0, (byte) 0xea, (byte) 0x09, (byte) 0x47, |
| (byte) 0x3e, (byte) 0xb9, (byte) 0xb6, (byte) 0xb8, (byte) 0x83, (byte) 0x9e, |
| (byte) 0xed, (byte) 0x59, (byte) 0xe5, (byte) 0xe7, (byte) 0x0f, (byte) 0xa1, |
| (byte) 0x64, (byte) 0x03, (byte) 0x62, (byte) 0x00, (byte) 0x04, (byte) 0x87, |
| (byte) 0x18, (byte) 0x55, (byte) 0x27, (byte) 0xb7, (byte) 0x5c, (byte) 0x4a, |
| (byte) 0xaa, (byte) 0xed, (byte) 0xa3, (byte) 0x45, (byte) 0xfe, (byte) 0x76, |
| (byte) 0x04, (byte) 0x71, (byte) 0xb5, (byte) 0xdf, (byte) 0x01, (byte) 0x58, |
| (byte) 0x6f, (byte) 0x1c, (byte) 0x5a, (byte) 0x63, (byte) 0xe3, (byte) 0x06, |
| (byte) 0x38, (byte) 0xb7, (byte) 0x30, (byte) 0x8c, (byte) 0x43, (byte) 0x2d, |
| (byte) 0xfa, (byte) 0x5c, (byte) 0x30, (byte) 0x3a, (byte) 0x08, (byte) 0x07, |
| (byte) 0xce, (byte) 0x1a, (byte) 0x7b, (byte) 0x51, (byte) 0x30, (byte) 0x80, |
| (byte) 0x99, (byte) 0xf8, (byte) 0x61, (byte) 0x46, (byte) 0x07, (byte) 0xad, |
| (byte) 0x6b, (byte) 0x14, (byte) 0x0e, (byte) 0x25, (byte) 0x4d, (byte) 0x8a, |
| (byte) 0x1b, (byte) 0xfc, (byte) 0x8f, (byte) 0xb6, (byte) 0xbf, (byte) 0x3a, |
| (byte) 0x5d, (byte) 0x6a, (byte) 0x70, (byte) 0x86, (byte) 0x36, (byte) 0xc9, |
| (byte) 0xb1, (byte) 0xfb, (byte) 0xb5, (byte) 0xb7, (byte) 0xb3, (byte) 0x79, |
| (byte) 0xf2, (byte) 0xe6, (byte) 0x6a, (byte) 0x0a, (byte) 0x80, (byte) 0xe2, |
| (byte) 0xf5, (byte) 0xc4, (byte) 0x51, (byte) 0x76, (byte) 0x57, (byte) 0xa1, |
| (byte) 0x4c, (byte) 0x8b, (byte) 0xe1, (byte) 0x93, (byte) 0x42, (byte) 0x0d, |
| (byte) 0x67, (byte) 0x1d, (byte) 0xc3, (byte) 0xa2, (byte) 0xeb |
| }; |
| public static final PrivateKey CLIENT_SUITE_B_ECC_KEY = |
| loadPrivateKey("EC", CLIENT_SUITE_B_ECC_KEY_DATA); |
| |
| private static X509Certificate loadCertificate(String blob) { |
| try { |
| final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); |
| InputStream stream = new ByteArrayInputStream(blob.getBytes(StandardCharsets.UTF_8)); |
| |
| return (X509Certificate) certFactory.generateCertificate(stream); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| return null; |
| } |
| } |
| |
| private static PrivateKey loadPrivateKey(String algorithm, byte[] fakeKey) { |
| try { |
| KeyFactory kf = KeyFactory.getInstance(algorithm); |
| return kf.generatePrivate(new PKCS8EncodedKeySpec(fakeKey)); |
| } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { |
| return null; |
| } |
| } |
| |
| private static boolean sWasVerboseLoggingEnabled; |
| private static boolean sWasScanThrottleEnabled; |
| private static WifiConfiguration sTestNetwork; |
| private static boolean sShouldRunTest = false; |
| |
| private Context mContext; |
| private WifiManager mWifiManager; |
| private ConnectivityManager mConnectivityManager; |
| private UiDevice mUiDevice; |
| private ConnectivityManager.NetworkCallback mNrNetworkCallback; |
| private TestHelper mTestHelper; |
| |
| private static final int DURATION = 10_000; |
| |
| @BeforeClass |
| public static void setUpClass() throws Exception { |
| Context context = InstrumentationRegistry.getInstrumentation().getContext(); |
| // skip the test if WiFi is not supported |
| if (!WifiFeature.isWifiSupported(context)) return; |
| sShouldRunTest = true; |
| |
| WifiManager wifiManager = context.getSystemService(WifiManager.class); |
| assertThat(wifiManager).isNotNull(); |
| |
| // turn on verbose logging for tests |
| sWasVerboseLoggingEnabled = ShellIdentityUtils.invokeWithShellPermissions( |
| () -> wifiManager.isVerboseLoggingEnabled()); |
| ShellIdentityUtils.invokeWithShellPermissions( |
| () -> wifiManager.setVerboseLoggingEnabled(true)); |
| // Disable scan throttling for tests. |
| sWasScanThrottleEnabled = ShellIdentityUtils.invokeWithShellPermissions( |
| () -> wifiManager.isScanThrottleEnabled()); |
| ShellIdentityUtils.invokeWithShellPermissions( |
| () -> wifiManager.setScanThrottleEnabled(false)); |
| |
| // enable Wifi |
| if (!wifiManager.isWifiEnabled()) { |
| ShellIdentityUtils.invokeWithShellPermissions(() -> wifiManager.setWifiEnabled(true)); |
| } |
| PollingCheck.check("Wifi not enabled", DURATION, () -> wifiManager.isWifiEnabled()); |
| |
| // check we have >= 1 saved network |
| List<WifiConfiguration> savedNetworks = ShellIdentityUtils.invokeWithShellPermissions( |
| () -> wifiManager.getPrivilegedConfiguredNetworks()); |
| assertWithMessage("Need at least one saved network") |
| .that(savedNetworks.isEmpty()).isFalse(); |
| |
| // Pick any network in range. |
| sTestNetwork = TestHelper.findMatchingSavedNetworksWithBssid(wifiManager, savedNetworks, 2) |
| .get(0); |
| } |
| |
| private static void enableAllSavedNetworks(@NonNull WifiManager wifiManager) { |
| ShellIdentityUtils.invokeWithShellPermissions( |
| () -> { |
| for (WifiConfiguration savedNetwork : wifiManager.getConfiguredNetworks()) { |
| wifiManager.enableNetwork(savedNetwork.networkId, false); |
| } |
| }); |
| } |
| |
| private static void disableAllSavedNetworks(@NonNull WifiManager wifiManager) { |
| ShellIdentityUtils.invokeWithShellPermissions( |
| () -> { |
| for (WifiConfiguration savedNetwork : wifiManager.getConfiguredNetworks()) { |
| wifiManager.disableNetwork(savedNetwork.networkId); |
| } |
| }); |
| } |
| |
| @AfterClass |
| public static void tearDownClass() throws Exception { |
| if (!sShouldRunTest) return; |
| |
| Context context = InstrumentationRegistry.getInstrumentation().getContext(); |
| WifiManager wifiManager = context.getSystemService(WifiManager.class); |
| assertThat(wifiManager).isNotNull(); |
| |
| if (!wifiManager.isWifiEnabled()) { |
| ShellIdentityUtils.invokeWithShellPermissions(() -> wifiManager.setWifiEnabled(true)); |
| } |
| |
| // Re-enable networks. |
| enableAllSavedNetworks(wifiManager); |
| |
| ShellIdentityUtils.invokeWithShellPermissions( |
| () -> wifiManager.setScanThrottleEnabled(sWasScanThrottleEnabled)); |
| ShellIdentityUtils.invokeWithShellPermissions( |
| () -> wifiManager.setVerboseLoggingEnabled(sWasVerboseLoggingEnabled)); |
| } |
| |
| @Before |
| public void setUp() throws Exception { |
| assumeTrue(sShouldRunTest); |
| mContext = InstrumentationRegistry.getInstrumentation().getContext(); |
| mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); |
| mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); |
| mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); |
| mTestHelper = new TestHelper(mContext, mUiDevice); |
| |
| assumeTrue(WifiFeature.isWifiSupported(mContext)); |
| |
| // turn screen on |
| mTestHelper.turnScreenOn(); |
| |
| // Clear any existing app state before each test. |
| if (WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(mContext)) { |
| ShellIdentityUtils.invokeWithShellPermissions( |
| () -> mWifiManager.removeAppState(myUid(), mContext.getPackageName())); |
| } |
| |
| // Disconnect & disable auto-join on the saved network to prevent auto-connect from |
| // interfering with the test. |
| ShellIdentityUtils.invokeWithShellPermissions( |
| () -> mWifiManager.allowAutojoinGlobal(false)); |
| disableAllSavedNetworks(mWifiManager); |
| ShellIdentityUtils.invokeWithShellPermissions( |
| () -> mWifiManager.allowAutojoinGlobal(true)); |
| |
| // Wait for Wifi to be disconnected. |
| PollingCheck.check( |
| "Wifi not disconnected", |
| 20000, |
| () -> mWifiManager.getConnectionInfo().getNetworkId() == -1); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| if (!sShouldRunTest) return; |
| // If there is failure, ensure we unregister the previous request. |
| if (mNrNetworkCallback != null) { |
| mConnectivityManager.unregisterNetworkCallback(mNrNetworkCallback); |
| } |
| // Clear any existing app state after each test. |
| if (WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(mContext)) { |
| ShellIdentityUtils.invokeWithShellPermissions( |
| () -> mWifiManager.removeAppState(myUid(), mContext.getPackageName())); |
| } |
| mTestHelper.turnScreenOff(); |
| } |
| |
| private void testSuccessfulConnectionWithSpecifier(WifiNetworkSpecifier specifier) |
| throws Exception { |
| mNrNetworkCallback = mTestHelper.testConnectionFlowWithSpecifier( |
| sTestNetwork, specifier, false); |
| } |
| |
| private void testUserRejectionWithSpecifier(WifiNetworkSpecifier specifier) |
| throws Exception { |
| mNrNetworkCallback = mTestHelper.testConnectionFlowWithSpecifier( |
| sTestNetwork, specifier, true); |
| } |
| |
| /** |
| * Tests the entire connection flow using a specific SSID in the specifier. |
| */ |
| @Test |
| public void testConnectionWithSpecificSsid() throws Exception { |
| WifiNetworkSpecifier specifier = |
| TestHelper.createSpecifierBuilderWithCredentialFromSavedNetwork( |
| sTestNetwork, true) |
| .build(); |
| assertThat(specifier.getPreferredChannelFrequenciesMhz().length != 0).isTrue(); |
| assertThat(ScanResult.convertFrequencyMhzToChannelIfSupported(specifier |
| .getPreferredChannelFrequenciesMhz()[0]) != ScanResult.UNSPECIFIED).isTrue(); |
| testSuccessfulConnectionWithSpecifier(specifier); |
| } |
| |
| /** |
| * Tests the entire connection flow using a SSID pattern in the specifier. |
| */ |
| @Test |
| public void testConnectionWithSsidPattern() throws Exception { |
| // Creates a ssid pattern by dropping the last char in the saved network & pass that |
| // as a prefix match pattern in the request. |
| String ssidUnquoted = WifiInfo.sanitizeSsid(sTestNetwork.SSID); |
| assertThat(ssidUnquoted.length()).isAtLeast(2); |
| String ssidPrefix = ssidUnquoted.substring(0, ssidUnquoted.length() - 1); |
| // Note: The match may return more than 1 network in this case since we use a prefix match, |
| // But, we will still ensure that the UI interactions in the test still selects the |
| // saved network for connection. |
| WifiNetworkSpecifier specifier = |
| TestHelper.createSpecifierBuilderWithCredentialFromSavedNetwork(sTestNetwork, true) |
| .setSsidPattern(new PatternMatcher(ssidPrefix, PatternMatcher.PATTERN_PREFIX)) |
| .build(); |
| assertThat(specifier.getPreferredChannelFrequenciesMhz().length != 0).isTrue(); |
| assertThat(ScanResult.convertFrequencyMhzToChannelIfSupported(specifier |
| .getPreferredChannelFrequenciesMhz()[0]) != ScanResult.UNSPECIFIED).isTrue(); |
| testSuccessfulConnectionWithSpecifier(specifier); |
| } |
| |
| /** |
| * Tests the entire connection flow using a specific BSSID in the specifier. |
| */ |
| @Test |
| public void testConnectionWithSpecificBssid() throws Exception { |
| WifiNetworkSpecifier specifier = |
| TestHelper.createSpecifierBuilderWithCredentialFromSavedNetworkWithBssid( |
| sTestNetwork) |
| .build(); |
| testSuccessfulConnectionWithSpecifier(specifier); |
| } |
| |
| /** |
| * Tests the entire connection flow using a BSSID pattern in the specifier. |
| */ |
| @Test |
| public void testConnectionWithBssidPattern() throws Exception { |
| // Note: The match may return more than 1 network in this case since we use a prefix match, |
| // But, we will still ensure that the UI interactions in the test still selects the |
| // saved network for connection. |
| WifiNetworkSpecifier specifier = |
| TestHelper.createSpecifierBuilderWithCredentialFromSavedNetworkWithBssid( |
| sTestNetwork) |
| .setBssidPattern(MacAddress.fromString(sTestNetwork.BSSID), |
| MacAddress.fromString("ff:ff:ff:00:00:00")) |
| .build(); |
| testSuccessfulConnectionWithSpecifier(specifier); |
| } |
| |
| /** |
| * Tests the entire connection flow using a BSSID pattern in the specifier. |
| */ |
| @Test |
| public void testUserRejectionWithSpecificSsid() throws Exception { |
| WifiNetworkSpecifier specifier = |
| TestHelper.createSpecifierBuilderWithCredentialFromSavedNetwork( |
| sTestNetwork, true) |
| .build(); |
| assertThat(specifier.getPreferredChannelFrequenciesMhz().length != 0).isTrue(); |
| assertThat(ScanResult.convertFrequencyMhzToChannelIfSupported(specifier |
| .getPreferredChannelFrequenciesMhz()[0]) != ScanResult.UNSPECIFIED).isTrue(); |
| testUserRejectionWithSpecifier(specifier); |
| } |
| |
| /** |
| * Tests using the specifier to set a band. |
| */ |
| @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) |
| @Test |
| public void testWifiBandInNetworkCallback() throws Exception { |
| // Enable all networks and wait for Internet connectivity to be restored. |
| // The callbacks in this test will match the existing network as soon as they are filed. |
| enableAllSavedNetworks(mWifiManager); |
| mTestHelper.assertWifiInternetConnectionAvailable(); |
| |
| final LinkedBlockingQueue<Pair<Integer, Integer>> results = new LinkedBlockingQueue<>(); |
| final int[] bands = { ScanResult.WIFI_BAND_24_GHZ, ScanResult.WIFI_BAND_5_GHZ, |
| ScanResult.WIFI_BAND_6_GHZ, ScanResult.WIFI_BAND_60_GHZ }; |
| final ArrayList<NetworkCallback> registeredCallbacks = new ArrayList<>(); |
| for (final int band : bands) { |
| final NetworkCallback callback = new NetworkCallback() { |
| @Override public void onCapabilitiesChanged(final Network net, |
| final NetworkCapabilities caps) { |
| results.offer(new Pair(band, TestHelper.getBandFromFrequency( |
| ((WifiInfo) caps.getTransportInfo()).getFrequency()))); |
| } |
| }; |
| |
| final WifiNetworkSpecifier specifier = |
| new WifiNetworkSpecifier.Builder().setBand(band).build(); |
| assertThat(specifier.getBand()).isEqualTo(band); |
| |
| final NetworkRequest request = new NetworkRequest.Builder() |
| .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) |
| .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) |
| .setNetworkSpecifier(specifier) |
| .build(); |
| mConnectivityManager.registerNetworkCallback(request, callback); |
| registeredCallbacks.add(callback); |
| } |
| |
| try { |
| // There should be at least one network callback about availability of the wifi network |
| // on the right band. If the device is currently connected to multiple WiFi networks, |
| // there will be several. Wait for a relatively long time for any callback, but only |
| // a short time for subsequent ones (as the last timeout will be incurred always). |
| Pair<Integer, Integer> result = results.poll(10, TimeUnit.SECONDS); |
| assertThat(result).isNotNull(); |
| while (null != result) { |
| assertThat(result.first).isEqualTo(result.second); |
| result = results.poll(200, TimeUnit.MILLISECONDS); |
| } |
| } finally { |
| for (final NetworkCallback cb : registeredCallbacks) { |
| mConnectivityManager.unregisterNetworkCallback(cb); |
| } |
| } |
| } |
| |
| /** |
| * Tests the entire connection flow using a specific SSID in the specifier and ensure that the |
| * device auto connects back to some saved network or suggestions in range of the device (that |
| * can provide internet connectivity) when the request is released. |
| */ |
| @Test |
| public void testEnsureAutoConnectToInternetConnectionOnRelease() throws Exception { |
| WifiNetworkSpecifier specifier = |
| TestHelper.createSpecifierBuilderWithCredentialFromSavedNetwork( |
| sTestNetwork, true) |
| .build(); |
| assertThat(specifier.getPreferredChannelFrequenciesMhz().length != 0).isTrue(); |
| assertThat(ScanResult.convertFrequencyMhzToChannelIfSupported(specifier |
| .getPreferredChannelFrequenciesMhz()[0]) != ScanResult.UNSPECIFIED).isTrue(); |
| testSuccessfulConnectionWithSpecifier(specifier); |
| |
| // Now release the network request. |
| mConnectivityManager.unregisterNetworkCallback(mNrNetworkCallback); |
| mNrNetworkCallback = null; |
| |
| // Enable all saved networks on the device |
| enableAllSavedNetworks(mWifiManager); |
| try { |
| // Wait for the device to auto-connect back to some saved or suggested network (which |
| // can provide internet connectivity. |
| // Note: On devices with concurrency support, this may return true immediately (since |
| // the internet connection may be present concurrently). |
| mTestHelper.assertWifiInternetConnectionAvailable(); |
| } finally { |
| // need to always disable saved networks again since the other tests in this class |
| // assume it |
| disableAllSavedNetworks(mWifiManager); |
| } |
| } |
| |
| /** |
| * Tests the builder for WPA2 enterprise networks. |
| * Note: Can't do end to end tests for such networks in CTS environment. |
| */ |
| @Test |
| public void testBuilderForWpa2Enterprise() { |
| WifiNetworkSpecifier specifier1 = new WifiNetworkSpecifier.Builder() |
| .setSsid(WifiInfo.sanitizeSsid(sTestNetwork.SSID)) |
| .setWpa2EnterpriseConfig(new WifiEnterpriseConfig()) |
| .build(); |
| WifiNetworkSpecifier specifier2 = new WifiNetworkSpecifier.Builder() |
| .setSsid(WifiInfo.sanitizeSsid(sTestNetwork.SSID)) |
| .setWpa2EnterpriseConfig(new WifiEnterpriseConfig()) |
| .build(); |
| assertThat(specifier1.canBeSatisfiedBy(specifier2)).isTrue(); |
| } |
| |
| /** |
| * Tests the builder for WPA3 enterprise networks. |
| * Note: Can't do end to end tests for such networks in CTS environment. |
| */ |
| @Test |
| public void testBuilderForWpa3Enterprise() { |
| WifiNetworkSpecifier specifier1 = new WifiNetworkSpecifier.Builder() |
| .setSsid(WifiInfo.sanitizeSsid(sTestNetwork.SSID)) |
| .setWpa3EnterpriseConfig(new WifiEnterpriseConfig()) |
| .build(); |
| WifiNetworkSpecifier specifier2 = new WifiNetworkSpecifier.Builder() |
| .setSsid(WifiInfo.sanitizeSsid(sTestNetwork.SSID)) |
| .setWpa3EnterpriseConfig(new WifiEnterpriseConfig()) |
| .build(); |
| assertThat(specifier1.canBeSatisfiedBy(specifier2)).isTrue(); |
| } |
| |
| /** |
| * Tests the builder for WPA3 enterprise networks. |
| * Note: Can't do end to end tests for such networks in CTS environment. |
| */ |
| @Test |
| public void testBuilderForWpa3EnterpriseWithStandardApi() { |
| if (!WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(mContext)) { |
| // Skip the test if wifi module version is older than S. |
| return; |
| } |
| WifiNetworkSpecifier specifier1 = new WifiNetworkSpecifier.Builder() |
| .setSsid(WifiInfo.sanitizeSsid(sTestNetwork.SSID)) |
| .setWpa3EnterpriseStandardModeConfig(new WifiEnterpriseConfig()) |
| .build(); |
| WifiNetworkSpecifier specifier2 = new WifiNetworkSpecifier.Builder() |
| .setSsid(WifiInfo.sanitizeSsid(sTestNetwork.SSID)) |
| .setWpa3EnterpriseConfig(new WifiEnterpriseConfig()) |
| .build(); |
| assertThat(specifier1.canBeSatisfiedBy(specifier2)).isTrue(); |
| } |
| |
| /** |
| * Tests the builder for WPA3 enterprise networks. |
| * Note: Can't do end to end tests for such networks in CTS environment. |
| */ |
| @Test |
| public void testBuilderForWpa3Enterprise192bit() { |
| if (!WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(mContext)) { |
| // Skip the test if wifi module version is older than S. |
| return; |
| } |
| WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); |
| enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS); |
| enterpriseConfig.setCaCertificate(CA_SUITE_B_ECDSA_CERT); |
| enterpriseConfig.setClientKeyEntryWithCertificateChain(CLIENT_SUITE_B_ECC_KEY, |
| new X509Certificate[] {CLIENT_SUITE_B_ECDSA_CERT}); |
| enterpriseConfig.setAltSubjectMatch("domain.com"); |
| |
| WifiNetworkSpecifier specifier1 = new WifiNetworkSpecifier.Builder() |
| .setSsid(WifiInfo.sanitizeSsid(sTestNetwork.SSID)) |
| .setWpa3Enterprise192BitModeConfig(enterpriseConfig) |
| .build(); |
| WifiNetworkSpecifier specifier2 = new WifiNetworkSpecifier.Builder() |
| .setSsid(WifiInfo.sanitizeSsid(sTestNetwork.SSID)) |
| .setWpa3Enterprise192BitModeConfig(enterpriseConfig) |
| .build(); |
| assertThat(specifier1.canBeSatisfiedBy(specifier2)).isTrue(); |
| } |
| |
| /** |
| * Test WifiNetworkSpecifier redaction. |
| */ |
| @Test |
| public void testRedact() { |
| if (!WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(mContext)) { |
| // Skip the test if wifi module version is older than S. |
| return; |
| } |
| WifiNetworkSpecifier specifier = TestHelper |
| .createSpecifierBuilderWithCredentialFromSavedNetworkWithBssid(sTestNetwork) |
| .setBssidPattern(MacAddress.fromString(sTestNetwork.BSSID), |
| MacAddress.fromString("ff:ff:ff:00:00:00")) |
| .setBand(ScanResult.WIFI_BAND_5_GHZ) |
| .build(); |
| assertThat(specifier.getPreferredChannelFrequenciesMhz().length == 0).isTrue(); |
| |
| final NetworkSpecifier redacted = specifier.redact(); |
| if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S)) { |
| assertThat(new WifiNetworkSpecifier.Builder().setBand(ScanResult.WIFI_BAND_5_GHZ) |
| .build().equals(redacted)).isTrue(); |
| } else { |
| assertThat(redacted.equals(specifier)).isTrue(); |
| } |
| } |
| } |