| /* |
| * Copyright (C) 2016 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 com.android.server.wifi; |
| |
| import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID; |
| import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP; |
| import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE; |
| import static android.net.wifi.WifiManager.ALL_ZEROS_MAC_ADDRESS; |
| |
| import static com.android.server.wifi.util.NativeUtil.addEnclosingQuotes; |
| |
| import android.annotation.SuppressLint; |
| import android.net.IpConfiguration; |
| import android.net.MacAddress; |
| import android.net.StaticIpConfiguration; |
| import android.net.wifi.SecurityParams; |
| 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.net.wifi.WifiScanner; |
| import android.os.PatternMatcher; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.util.Pair; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.modules.utils.build.SdkLevel; |
| import com.android.server.wifi.util.NativeUtil; |
| |
| import java.nio.charset.StandardCharsets; |
| import java.security.cert.X509Certificate; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.BitSet; |
| import java.util.Comparator; |
| import java.util.List; |
| import java.util.Objects; |
| |
| /** |
| * WifiConfiguration utility for any {@link android.net.wifi.WifiConfiguration} related operations. |
| * Currently contains: |
| * > Helper method to check if the WifiConfiguration object is visible to the provided users. |
| * > Helper methods to identify the encryption of a WifiConfiguration object. |
| */ |
| public class WifiConfigurationUtil { |
| private static final String TAG = "WifiConfigurationUtil"; |
| |
| /** |
| * Constants used for validating external config objects. |
| */ |
| private static final int ENCLOSING_QUOTES_LEN = 2; |
| private static final int SSID_UTF_8_MIN_LEN = 1 + ENCLOSING_QUOTES_LEN; |
| private static final int SSID_UTF_8_MAX_LEN = 32 + ENCLOSING_QUOTES_LEN; |
| private static final int SSID_HEX_MIN_LEN = 2; |
| private static final int SSID_HEX_MAX_LEN = 64; |
| private static final int PSK_ASCII_MIN_LEN = 8 + ENCLOSING_QUOTES_LEN; |
| private static final int SAE_ASCII_MIN_LEN = 1 + ENCLOSING_QUOTES_LEN; |
| private static final int PSK_SAE_ASCII_MAX_LEN = 63 + ENCLOSING_QUOTES_LEN; |
| private static final int PSK_SAE_HEX_LEN = 64; |
| private static final int WEP104_KEY_BYTES_LEN = 13; |
| private static final int WEP40_KEY_BYTES_LEN = 5; |
| |
| @VisibleForTesting |
| public static final String PASSWORD_MASK = "*"; |
| private static final String MATCH_EMPTY_SSID_PATTERN_PATH = ""; |
| private static final Pair<MacAddress, MacAddress> MATCH_NONE_BSSID_PATTERN = |
| new Pair<>(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS); |
| private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN = |
| new Pair<>(ALL_ZEROS_MAC_ADDRESS, ALL_ZEROS_MAC_ADDRESS); |
| |
| private static final int NETWORK_ID_SECURITY_MASK = 0xff; |
| private static final int NETWORK_ID_SECURITY_OFFSET = 23; |
| |
| /** |
| * Checks if the provided |wepKeys| array contains any non-null value; |
| */ |
| public static boolean hasAnyValidWepKey(String[] wepKeys) { |
| for (int i = 0; i < wepKeys.length; i++) { |
| if (wepKeys[i] != null) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Helper method to check if the provided |config| corresponds to a PSK network or not. |
| */ |
| public static boolean isConfigForPskNetwork(WifiConfiguration config) { |
| return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK); |
| } |
| |
| /** |
| * Helper method to check if the provided |config| corresponds to a WAPI PSK network or not. |
| */ |
| public static boolean isConfigForWapiPskNetwork(WifiConfiguration config) { |
| return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WAPI_PSK); |
| } |
| |
| /** |
| * Helper method to check if the provided |config| corresponds to a WAPI CERT network or not. |
| */ |
| public static boolean isConfigForWapiCertNetwork(WifiConfiguration config) { |
| return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WAPI_CERT); |
| } |
| |
| /** |
| * Helper method to check if the provided |config| corresponds to an SAE network or not. |
| */ |
| public static boolean isConfigForSaeNetwork(WifiConfiguration config) { |
| return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE); |
| } |
| |
| /** |
| * Helper method to check if the provided |config| corresponds to an OWE network or not. |
| */ |
| public static boolean isConfigForOweNetwork(WifiConfiguration config) { |
| return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_OWE); |
| } |
| |
| /** |
| * Helper method to check if the provided |config| corresponds to a EAP network or not. |
| */ |
| public static boolean isConfigForEapNetwork(WifiConfiguration config) { |
| return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP); |
| } |
| |
| /** |
| * Helper method to check if the provided |config| corresponds to |
| * a WPA3 Enterprise network or not. |
| */ |
| public static boolean isConfigForWpa3EnterpriseNetwork(WifiConfiguration config) { |
| return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE); |
| } |
| |
| /** |
| * Helper method to check if the provided |config| corresponds to a EAP Suite-B network or not. |
| */ |
| public static boolean isConfigForWpa3Enterprise192BitNetwork(WifiConfiguration config) { |
| return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT); |
| } |
| |
| /** |
| * Helper method to check if the provided |config| corresponds to a WEP network or not. |
| */ |
| public static boolean isConfigForWepNetwork(WifiConfiguration config) { |
| return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WEP); |
| } |
| |
| /** |
| * Helper method to check if the provided |config| corresponds to an open or enhanced |
| * open network, or not. |
| */ |
| public static boolean isConfigForOpenNetwork(WifiConfiguration config) { |
| return (!(isConfigForWepNetwork(config) || isConfigForPskNetwork(config) |
| || isConfigForWapiPskNetwork(config) || isConfigForWapiCertNetwork(config) |
| || isConfigForEapNetwork(config) || isConfigForSaeNetwork(config) |
| || isConfigForWpa3Enterprise192BitNetwork(config))); |
| } |
| |
| /** |
| * Compare existing and new WifiConfiguration objects after a network update and return if |
| * IP parameters have changed or not. |
| * |
| * @param existingConfig Existing WifiConfiguration object corresponding to the network. |
| * @param newConfig New WifiConfiguration object corresponding to the network. |
| * @return true if IP parameters have changed, false otherwise. |
| */ |
| public static boolean hasIpChanged(WifiConfiguration existingConfig, |
| WifiConfiguration newConfig) { |
| if (existingConfig.getIpAssignment() != newConfig.getIpAssignment()) { |
| return true; |
| } |
| if (newConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC) { |
| return !Objects.equals(existingConfig.getStaticIpConfiguration(), |
| newConfig.getStaticIpConfiguration()); |
| } |
| return false; |
| } |
| |
| /** |
| * Compare existing and new WifiConfiguration objects after a network update and return if |
| * proxy parameters have changed or not. |
| * |
| * @param existingConfig Existing WifiConfiguration object corresponding to the network. |
| * @param newConfig New WifiConfiguration object corresponding to the network. |
| * @return true if proxy parameters have changed, false if no existing config and proxy settings |
| * are NONE, false otherwise. |
| */ |
| public static boolean hasProxyChanged(WifiConfiguration existingConfig, |
| WifiConfiguration newConfig) { |
| if (existingConfig == null) { |
| return newConfig.getProxySettings() != IpConfiguration.ProxySettings.NONE; |
| } |
| if (newConfig.getProxySettings() != existingConfig.getProxySettings()) { |
| return true; |
| } |
| return !Objects.equals(existingConfig.getHttpProxy(), newConfig.getHttpProxy()); |
| } |
| |
| /** |
| * Compare existing and new WifiConfiguration objects after a network update and return if |
| * MAC randomization setting has changed or not. |
| * @param existingConfig Existing WifiConfiguration object corresponding to the network. |
| * @param newConfig New WifiConfiguration object corresponding to the network. |
| * @return true if MAC randomization setting setting changed or the existing confiuration is |
| * null and the newConfig is setting macRandomizationSetting to the default value. |
| */ |
| public static boolean hasMacRandomizationSettingsChanged(WifiConfiguration existingConfig, |
| WifiConfiguration newConfig) { |
| if (existingConfig == null) { |
| return newConfig.macRandomizationSetting != WifiConfiguration.RANDOMIZATION_AUTO; |
| } |
| return newConfig.macRandomizationSetting != existingConfig.macRandomizationSetting; |
| } |
| |
| /** |
| * Compare existing and new WifiEnterpriseConfig objects after a network update and return if |
| * credential parameters have changed or not. |
| * |
| * @param existingEnterpriseConfig Existing WifiConfiguration object corresponding to the |
| * network. |
| * @param newEnterpriseConfig New WifiConfiguration object corresponding to the network. |
| * @return true if credentials have changed, false otherwise. |
| */ |
| @VisibleForTesting |
| public static boolean hasEnterpriseConfigChanged(WifiEnterpriseConfig existingEnterpriseConfig, |
| WifiEnterpriseConfig newEnterpriseConfig) { |
| if (existingEnterpriseConfig != null && newEnterpriseConfig != null) { |
| if (existingEnterpriseConfig.getEapMethod() != newEnterpriseConfig.getEapMethod()) { |
| return true; |
| } |
| if (existingEnterpriseConfig.isAuthenticationSimBased()) { |
| // No other credential changes for SIM based methods. |
| // The SIM card is the credential. |
| return false; |
| } |
| if (existingEnterpriseConfig.getPhase2Method() |
| != newEnterpriseConfig.getPhase2Method()) { |
| return true; |
| } |
| if (!TextUtils.equals(existingEnterpriseConfig.getIdentity(), |
| newEnterpriseConfig.getIdentity())) { |
| return true; |
| } |
| if (!TextUtils.equals(existingEnterpriseConfig.getAnonymousIdentity(), |
| newEnterpriseConfig.getAnonymousIdentity())) { |
| return true; |
| } |
| if (!TextUtils.equals(existingEnterpriseConfig.getPassword(), |
| newEnterpriseConfig.getPassword())) { |
| return true; |
| } |
| X509Certificate[] existingCaCerts = existingEnterpriseConfig.getCaCertificates(); |
| X509Certificate[] newCaCerts = newEnterpriseConfig.getCaCertificates(); |
| if (!Arrays.equals(existingCaCerts, newCaCerts)) { |
| return true; |
| } |
| if (!Arrays.equals(newEnterpriseConfig.getCaCertificateAliases(), |
| existingEnterpriseConfig.getCaCertificateAliases())) { |
| return true; |
| } |
| if (!TextUtils.equals(newEnterpriseConfig.getClientCertificateAlias(), |
| existingEnterpriseConfig.getClientCertificateAlias())) { |
| return true; |
| } |
| if (!TextUtils.equals(newEnterpriseConfig.getAltSubjectMatch(), |
| existingEnterpriseConfig.getAltSubjectMatch())) { |
| return true; |
| } |
| if (!TextUtils.equals(newEnterpriseConfig.getWapiCertSuite(), |
| existingEnterpriseConfig.getWapiCertSuite())) { |
| return true; |
| } |
| if (newEnterpriseConfig.getOcsp() != existingEnterpriseConfig.getOcsp()) { |
| return true; |
| } |
| } else { |
| // One of the configs may have an enterpriseConfig |
| if (existingEnterpriseConfig != null || newEnterpriseConfig != null) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Compare existing and new WifiConfiguration objects after a network update and return if |
| * credential parameters have changed or not. |
| * |
| * @param existingConfig Existing WifiConfiguration object corresponding to the network. |
| * @param newConfig New WifiConfiguration object corresponding to the network. |
| * @return true if credentials have changed, false otherwise. |
| */ |
| public static boolean hasCredentialChanged(WifiConfiguration existingConfig, |
| WifiConfiguration newConfig) { |
| if (!Objects.equals(existingConfig.allowedKeyManagement, |
| newConfig.allowedKeyManagement)) { |
| return true; |
| } |
| if (!Objects.equals(existingConfig.allowedProtocols, newConfig.allowedProtocols)) { |
| return true; |
| } |
| if (!Objects.equals(existingConfig.allowedAuthAlgorithms, |
| newConfig.allowedAuthAlgorithms)) { |
| return true; |
| } |
| if (!Objects.equals(existingConfig.allowedPairwiseCiphers, |
| newConfig.allowedPairwiseCiphers)) { |
| return true; |
| } |
| if (!Objects.equals(existingConfig.allowedGroupCiphers, |
| newConfig.allowedGroupCiphers)) { |
| return true; |
| } |
| if (!Objects.equals(existingConfig.allowedGroupManagementCiphers, |
| newConfig.allowedGroupManagementCiphers)) { |
| return true; |
| } |
| if (!Objects.equals(existingConfig.allowedSuiteBCiphers, |
| newConfig.allowedSuiteBCiphers)) { |
| return true; |
| } |
| if (!existingConfig.getSecurityParamsList().equals(newConfig.getSecurityParamsList())) { |
| return true; |
| } |
| if (!Objects.equals(existingConfig.preSharedKey, newConfig.preSharedKey)) { |
| return true; |
| } |
| if (!Arrays.equals(existingConfig.wepKeys, newConfig.wepKeys)) { |
| return true; |
| } |
| if (existingConfig.wepTxKeyIndex != newConfig.wepTxKeyIndex) { |
| return true; |
| } |
| if (existingConfig.hiddenSSID != newConfig.hiddenSSID) { |
| return true; |
| } |
| if (existingConfig.requirePmf != newConfig.requirePmf) { |
| return true; |
| } |
| if (existingConfig.carrierId != newConfig.carrierId) { |
| return true; |
| } |
| if (hasEnterpriseConfigChanged(existingConfig.enterpriseConfig, |
| newConfig.enterpriseConfig)) { |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean validateSsid(String ssid, boolean isAdd) { |
| if (isAdd) { |
| if (ssid == null) { |
| Log.e(TAG, "validateSsid : null string"); |
| return false; |
| } |
| } else { |
| if (ssid == null) { |
| // This is an update, so the SSID can be null if that is not being changed. |
| return true; |
| } |
| } |
| if (ssid.isEmpty()) { |
| Log.e(TAG, "validateSsid failed: empty string"); |
| return false; |
| } |
| if (ssid.startsWith("\"")) { |
| // UTF-8 SSID string |
| byte[] ssidBytes = ssid.getBytes(StandardCharsets.UTF_8); |
| if (ssidBytes.length < SSID_UTF_8_MIN_LEN) { |
| Log.e(TAG, "validateSsid failed: utf-8 ssid string size too small: " |
| + ssidBytes.length); |
| return false; |
| } |
| if (ssidBytes.length > SSID_UTF_8_MAX_LEN) { |
| Log.e(TAG, "validateSsid failed: utf-8 ssid string size too large: " |
| + ssidBytes.length); |
| return false; |
| } |
| } else { |
| // HEX SSID string |
| if (ssid.length() < SSID_HEX_MIN_LEN) { |
| Log.e(TAG, "validateSsid failed: hex string size too small: " + ssid.length()); |
| return false; |
| } |
| if (ssid.length() > SSID_HEX_MAX_LEN) { |
| Log.e(TAG, "validateSsid failed: hex string size too large: " + ssid.length()); |
| return false; |
| } |
| } |
| try { |
| NativeUtil.decodeSsid(ssid); |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "validateSsid failed: malformed string: " + ssid); |
| return false; |
| } |
| return true; |
| } |
| |
| private static boolean validateBssid(MacAddress bssid) { |
| if (bssid == null) return true; |
| if (bssid.getAddressType() != MacAddress.TYPE_UNICAST) { |
| Log.e(TAG, "validateBssid failed: invalid bssid"); |
| return false; |
| } |
| return true; |
| } |
| |
| private static boolean validateBssid(String bssid) { |
| if (bssid == null) return true; |
| if (bssid.isEmpty()) { |
| Log.e(TAG, "validateBssid failed: empty string"); |
| return false; |
| } |
| // Allow reset of bssid with "any". |
| if (bssid.equals(ClientModeImpl.SUPPLICANT_BSSID_ANY)) return true; |
| MacAddress bssidMacAddress; |
| try { |
| bssidMacAddress = MacAddress.fromString(bssid); |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "validateBssid failed: malformed string: " + bssid); |
| return false; |
| } |
| if (!validateBssid(bssidMacAddress)) { |
| return false; |
| } |
| return true; |
| } |
| |
| private static boolean validatePassword(String password, boolean isAdd, boolean isSae) { |
| if (isAdd) { |
| if (password == null) { |
| Log.e(TAG, "validatePassword: null string"); |
| return false; |
| } |
| } else { |
| if (password == null) { |
| // This is an update, so the psk can be null if that is not being changed. |
| return true; |
| } else if (password.equals(PASSWORD_MASK)) { |
| // This is an update, so the app might have returned back the masked password, let |
| // it thru. WifiConfigManager will handle it. |
| return true; |
| } |
| } |
| if (password.isEmpty()) { |
| Log.e(TAG, "validatePassword failed: empty string"); |
| return false; |
| } |
| if (password.startsWith("\"")) { |
| // ASCII PSK string |
| byte[] passwordBytes = password.getBytes(StandardCharsets.US_ASCII); |
| int targetMinLength; |
| |
| if (isSae) { |
| targetMinLength = SAE_ASCII_MIN_LEN; |
| } else { |
| targetMinLength = PSK_ASCII_MIN_LEN; |
| } |
| if (passwordBytes.length < targetMinLength) { |
| Log.e(TAG, "validatePassword failed: ASCII string size too small: " |
| + passwordBytes.length); |
| return false; |
| } |
| if (passwordBytes.length > PSK_SAE_ASCII_MAX_LEN) { |
| Log.e(TAG, "validatePassword failed: ASCII string size too large: " |
| + passwordBytes.length); |
| return false; |
| } |
| } else { |
| // HEX PSK string |
| if (password.length() != PSK_SAE_HEX_LEN) { |
| Log.e(TAG, "validatePassword failed: hex string size mismatch: " |
| + password.length()); |
| return false; |
| } |
| } |
| try { |
| NativeUtil.hexOrQuotedStringToBytes(password); |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "validatePassword failed: malformed string: " + password); |
| return false; |
| } |
| return true; |
| } |
| |
| private static boolean validateWepKeys(String[] wepKeys, int wepTxKeyIndex, boolean isAdd) { |
| if (isAdd) { |
| if (wepKeys == null) { |
| Log.e(TAG, "validateWepKeys: null string"); |
| return false; |
| } |
| } else { |
| if (wepKeys == null) { |
| // This is an update, so the psk can be null if that is not being changed. |
| return true; |
| } else { |
| boolean allMaskedKeys = true; |
| for (int i = 0; i < wepKeys.length; i++) { |
| if (wepKeys[i] != null && !TextUtils.equals(wepKeys[i], PASSWORD_MASK)) { |
| allMaskedKeys = false; |
| } |
| } |
| if (allMaskedKeys) { |
| // This is an update, so the app might have returned back the masked password, |
| // let it thru. WifiConfigManager will handle it. |
| return true; |
| } |
| } |
| } |
| for (int i = 0; i < wepKeys.length; i++) { |
| if (wepKeys[i] != null) { |
| try { |
| ArrayList<Byte> wepKeyBytes = |
| NativeUtil.hexOrQuotedStringToBytes(wepKeys[i]); |
| if (wepKeyBytes.size() != WEP40_KEY_BYTES_LEN |
| && wepKeyBytes.size() != WEP104_KEY_BYTES_LEN) { |
| Log.e(TAG, "validateWepKeys: invalid wep key length " |
| + wepKeys[i].length() + " at index " + i); |
| return false; |
| } |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "validateWepKeys: invalid wep key at index " + i, e); |
| return false; |
| } |
| } |
| } |
| if (wepTxKeyIndex >= wepKeys.length) { |
| Log.e(TAG, "validateWepKeys: invalid wep tx key index " + wepTxKeyIndex |
| + " wepKeys len: " + wepKeys.length); |
| return false; |
| } |
| return true; |
| } |
| |
| private static boolean validateBitSet(BitSet bitSet, int validValuesLength) { |
| if (bitSet == null) return false; |
| BitSet clonedBitset = (BitSet) bitSet.clone(); |
| clonedBitset.clear(0, validValuesLength); |
| return clonedBitset.isEmpty(); |
| } |
| |
| private static boolean validateBitSets(WifiConfiguration config) { |
| // 1. Check |allowedKeyManagement|. |
| if (!validateBitSet(config.allowedKeyManagement, |
| WifiConfiguration.KeyMgmt.strings.length)) { |
| Log.e(TAG, "validateBitsets failed: invalid allowedKeyManagement bitset " |
| + config.allowedKeyManagement); |
| return false; |
| } |
| // 2. Check |allowedProtocols|. |
| if (!validateBitSet(config.allowedProtocols, |
| WifiConfiguration.Protocol.strings.length)) { |
| Log.e(TAG, "validateBitsets failed: invalid allowedProtocols bitset " |
| + config.allowedProtocols); |
| return false; |
| } |
| // 3. Check |allowedAuthAlgorithms|. |
| if (!validateBitSet(config.allowedAuthAlgorithms, |
| WifiConfiguration.AuthAlgorithm.strings.length)) { |
| Log.e(TAG, "validateBitsets failed: invalid allowedAuthAlgorithms bitset " |
| + config.allowedAuthAlgorithms); |
| return false; |
| } |
| // 4. Check |allowedGroupCiphers|. |
| if (!validateBitSet(config.allowedGroupCiphers, |
| WifiConfiguration.GroupCipher.strings.length)) { |
| Log.e(TAG, "validateBitsets failed: invalid allowedGroupCiphers bitset " |
| + config.allowedGroupCiphers); |
| return false; |
| } |
| // 5. Check |allowedPairwiseCiphers|. |
| if (!validateBitSet(config.allowedPairwiseCiphers, |
| WifiConfiguration.PairwiseCipher.strings.length)) { |
| Log.e(TAG, "validateBitsets failed: invalid allowedPairwiseCiphers bitset " |
| + config.allowedPairwiseCiphers); |
| return false; |
| } |
| return true; |
| } |
| |
| private static boolean validateKeyMgmt(BitSet keyMgmnt) { |
| if (keyMgmnt.cardinality() > 1) { |
| if (keyMgmnt.cardinality() > 3) { |
| Log.e(TAG, "validateKeyMgmt failed: cardinality > 3"); |
| return false; |
| } |
| if (!keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_EAP)) { |
| Log.e(TAG, "validateKeyMgmt failed: not WPA_EAP"); |
| return false; |
| } |
| if (!keyMgmnt.get(WifiConfiguration.KeyMgmt.IEEE8021X) |
| && !keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { |
| Log.e(TAG, "validateKeyMgmt failed: not PSK or 8021X"); |
| return false; |
| } |
| // SUITE-B keymgmt must be WPA_EAP + IEEE8021X + SUITE_B_192. |
| if (keyMgmnt.cardinality() == 3 |
| && !(keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_EAP) |
| && keyMgmnt.get(WifiConfiguration.KeyMgmt.IEEE8021X) |
| && keyMgmnt.get(WifiConfiguration.KeyMgmt.SUITE_B_192))) { |
| Log.e(TAG, "validateKeyMgmt failed: not SUITE_B_192"); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private static boolean validateIpConfiguration(IpConfiguration ipConfig) { |
| if (ipConfig == null) { |
| Log.e(TAG, "validateIpConfiguration failed: null IpConfiguration"); |
| return false; |
| } |
| if (ipConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC) { |
| StaticIpConfiguration staticIpConfig = ipConfig.getStaticIpConfiguration(); |
| if (staticIpConfig == null) { |
| Log.e(TAG, "validateIpConfiguration failed: null StaticIpConfiguration"); |
| return false; |
| } |
| if (staticIpConfig.getIpAddress() == null) { |
| Log.e(TAG, "validateIpConfiguration failed: null static ip Address"); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Enums to specify if the provided config is being validated for add or update. |
| */ |
| public static final boolean VALIDATE_FOR_ADD = true; |
| public static final boolean VALIDATE_FOR_UPDATE = false; |
| |
| /** |
| * Validate the configuration received from an external application. |
| * |
| * This method checks for the following parameters: |
| * 1. {@link WifiConfiguration#SSID} |
| * 2. {@link WifiConfiguration#BSSID} |
| * 3. {@link WifiConfiguration#preSharedKey} |
| * 4. {@link WifiConfiguration#allowedKeyManagement} |
| * 5. {@link WifiConfiguration#allowedProtocols} |
| * 6. {@link WifiConfiguration#allowedAuthAlgorithms} |
| * 7. {@link WifiConfiguration#allowedGroupCiphers} |
| * 8. {@link WifiConfiguration#allowedPairwiseCiphers} |
| * 9. {@link WifiConfiguration#getIpConfiguration()} |
| * |
| * @param config {@link WifiConfiguration} received from an external application. |
| * @param isAdd {@link #VALIDATE_FOR_ADD} to indicate a network config received for an add, |
| * {@link #VALIDATE_FOR_UPDATE} for a network config received for an update. |
| * These 2 cases need to be handled differently because the config received for an |
| * update could contain only the fields that are being changed. |
| * @return true if the parameters are valid, false otherwise. |
| */ |
| public static boolean validate(WifiConfiguration config, boolean isAdd) { |
| if (!validateSsid(config.SSID, isAdd)) { |
| return false; |
| } |
| if (!validateBssid(config.BSSID)) { |
| return false; |
| } |
| if (!validateBitSets(config)) { |
| return false; |
| } |
| if (!validateKeyMgmt(config.allowedKeyManagement)) { |
| return false; |
| } |
| if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WEP) |
| && config.wepKeys != null |
| && !validateWepKeys(config.wepKeys, config.wepTxKeyIndex, isAdd)) { |
| return false; |
| } |
| if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK) |
| && !validatePassword(config.preSharedKey, isAdd, false)) { |
| return false; |
| } |
| if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE) |
| && !validatePassword(config.preSharedKey, isAdd, true)) { |
| return false; |
| } |
| |
| if (!validateEnterpriseConfig(config, isAdd)) { |
| return false; |
| } |
| |
| // b/153435438: Added to deal with badly formed WifiConfiguration from apps. |
| if (config.preSharedKey != null && !config.needsPreSharedKey()) { |
| Log.e(TAG, "preSharedKey set with an invalid KeyMgmt, resetting KeyMgmt to WPA_PSK"); |
| config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); |
| } |
| if (!validateIpConfiguration(config.getIpConfiguration())) { |
| return false; |
| } |
| // TBD: Validate some enterprise params as well in the future here. |
| return true; |
| } |
| |
| private static boolean validateBssidPattern( |
| Pair<MacAddress, MacAddress> bssidPatternMatcher) { |
| if (bssidPatternMatcher == null) return true; |
| MacAddress baseAddress = bssidPatternMatcher.first; |
| MacAddress mask = bssidPatternMatcher.second; |
| if (baseAddress.getAddressType() != MacAddress.TYPE_UNICAST) { |
| Log.e(TAG, "validateBssidPatternMatcher failed : invalid base address: " + baseAddress); |
| return false; |
| } |
| if (mask.equals(ALL_ZEROS_MAC_ADDRESS) |
| && !baseAddress.equals(ALL_ZEROS_MAC_ADDRESS)) { |
| Log.e(TAG, "validateBssidPatternMatcher failed : invalid mask/base: " + mask + "/" |
| + baseAddress); |
| return false; |
| } |
| return true; |
| } |
| |
| // TODO(b/113878056): Some of this is duplicated in {@link WifiNetworkConfigBuilder}. |
| // Merge them somehow?. |
| private static boolean isValidNetworkSpecifier(WifiNetworkSpecifier specifier) { |
| PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher; |
| Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher; |
| if (ssidPatternMatcher == null || bssidPatternMatcher == null) { |
| return false; |
| } |
| if (ssidPatternMatcher.getPath() == null || bssidPatternMatcher.first == null |
| || bssidPatternMatcher.second == null) { |
| return false; |
| } |
| return true; |
| } |
| |
| private static boolean isMatchNoneNetworkSpecifier(WifiNetworkSpecifier specifier) { |
| PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher; |
| Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher; |
| if (ssidPatternMatcher.getType() != PatternMatcher.PATTERN_PREFIX |
| && ssidPatternMatcher.getPath().equals(MATCH_EMPTY_SSID_PATTERN_PATH)) { |
| return true; |
| } |
| if (bssidPatternMatcher.equals(MATCH_NONE_BSSID_PATTERN)) { |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean isMatchAllNetworkSpecifier(WifiNetworkSpecifier specifier) { |
| PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher; |
| Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher; |
| if (ssidPatternMatcher.match(MATCH_EMPTY_SSID_PATTERN_PATH) |
| && bssidPatternMatcher.equals(MATCH_ALL_BSSID_PATTERN)) { |
| return true; |
| } |
| return false; |
| } |
| |
| // TODO: b/177434707 calls inside same module are safe |
| @SuppressLint("NewApi") |
| private static int getBand(WifiNetworkSpecifier s) { |
| return s.getBand(); |
| } |
| |
| /** |
| * Validate the configuration received from an external application inside |
| * {@link WifiNetworkSpecifier}. |
| * |
| * This method checks for the following parameters: |
| * 1. {@link WifiNetworkSpecifier#ssidPatternMatcher} |
| * 2. {@link WifiNetworkSpecifier#bssidPatternMatcher} |
| * 3. {@link WifiNetworkSpecifier#getBand()} |
| * 4. {@link WifiConfiguration#SSID} |
| * 5. {@link WifiConfiguration#BSSID} |
| * 6. {@link WifiConfiguration#preSharedKey} |
| * 7. {@link WifiConfiguration#allowedKeyManagement} |
| * 8. {@link WifiConfiguration#allowedProtocols} |
| * 9. {@link WifiConfiguration#allowedAuthAlgorithms} |
| * 10. {@link WifiConfiguration#allowedGroupCiphers} |
| * 11. {@link WifiConfiguration#allowedPairwiseCiphers} |
| * 12. {@link WifiConfiguration#getIpConfiguration()} |
| * |
| * @param specifier Instance of {@link WifiNetworkSpecifier}. |
| * @return true if the parameters are valid, false otherwise. |
| */ |
| public static boolean validateNetworkSpecifier(WifiNetworkSpecifier specifier) { |
| if (!isValidNetworkSpecifier(specifier)) { |
| Log.e(TAG, "validateNetworkSpecifier failed : invalid network specifier"); |
| return false; |
| } |
| if (isMatchNoneNetworkSpecifier(specifier)) { |
| Log.e(TAG, "validateNetworkSpecifier failed : match-none specifier"); |
| return false; |
| } |
| if (isMatchAllNetworkSpecifier(specifier)) { |
| Log.e(TAG, "validateNetworkSpecifier failed : match-all specifier"); |
| return false; |
| } |
| if (!WifiNetworkSpecifier.validateBand(getBand(specifier))) { |
| return false; |
| } |
| WifiConfiguration config = specifier.wifiConfiguration; |
| if (specifier.ssidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) { |
| // For literal SSID matches, the value should satisfy SSID requirements. |
| // WifiConfiguration.SSID needs quotes around ASCII SSID. |
| if (!validateSsid(addEnclosingQuotes(specifier.ssidPatternMatcher.getPath()), true)) { |
| return false; |
| } |
| } else { |
| if (config.hiddenSSID) { |
| Log.e(TAG, "validateNetworkSpecifier failed : ssid pattern not supported " |
| + "for hidden networks"); |
| return false; |
| } |
| } |
| if (Objects.equals(specifier.bssidPatternMatcher.second, MacAddress.BROADCAST_ADDRESS)) { |
| // For literal BSSID matches, the value should satisfy MAC address requirements. |
| if (!validateBssid(specifier.bssidPatternMatcher.first)) { |
| return false; |
| } |
| } else { |
| if (!validateBssidPattern(specifier.bssidPatternMatcher)) { |
| return false; |
| } |
| } |
| if (!validateBitSets(config)) { |
| return false; |
| } |
| if (!validateKeyMgmt(config.allowedKeyManagement)) { |
| return false; |
| } |
| if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK) |
| && !validatePassword(config.preSharedKey, true, false)) { |
| return false; |
| } |
| if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE) |
| && !validatePassword(config.preSharedKey, true, true)) { |
| return false; |
| } |
| // TBD: Validate some enterprise params as well in the future here. |
| return true; |
| } |
| |
| /** |
| * Check if the provided two networks are the same. |
| * Note: This does not check if network selection BSSID's are the same. |
| * |
| * @param config Configuration corresponding to a network. |
| * @param config1 Configuration corresponding to another network. |
| * |
| * @return true if |config| and |config1| are the same network. |
| * false otherwise. |
| */ |
| public static boolean isSameNetwork(WifiConfiguration config, WifiConfiguration config1) { |
| if (config == null && config1 == null) { |
| return true; |
| } |
| if (config == null || config1 == null) { |
| return false; |
| } |
| if (config.networkId != config1.networkId) { |
| return false; |
| } |
| if (!Objects.equals(config.SSID, config1.SSID)) { |
| return false; |
| } |
| if (!Objects.equals(config.getNetworkSelectionStatus().getCandidateSecurityParams(), |
| config1.getNetworkSelectionStatus().getCandidateSecurityParams())) { |
| return false; |
| } |
| if (WifiConfigurationUtil.hasCredentialChanged(config, config1)) { |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Create a PnoNetwork object from the provided WifiConfiguration. |
| * |
| * @param config Configuration corresponding to the network. |
| * @return PnoNetwork object corresponding to the network. |
| */ |
| public static WifiScanner.PnoSettings.PnoNetwork createPnoNetwork( |
| WifiConfiguration config) { |
| WifiScanner.PnoSettings.PnoNetwork pnoNetwork = |
| new WifiScanner.PnoSettings.PnoNetwork(config.SSID); |
| if (config.hiddenSSID) { |
| pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN; |
| } |
| pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_A_BAND; |
| pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_G_BAND; |
| if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)) { |
| pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_PSK; |
| } else if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP)) { |
| pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_EAPOL; |
| } else { |
| pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_OPEN; |
| } |
| return pnoNetwork; |
| } |
| |
| private static void addOpenUpgradableSecurityTypeIfNecessary(WifiConfiguration config) { |
| if (!config.isSecurityType(WifiConfiguration.SECURITY_TYPE_OPEN)) return; |
| if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_OWE)) return; |
| |
| Log.d(TAG, "Add upgradable OWE configuration."); |
| SecurityParams oweParams = SecurityParams.createSecurityParamsBySecurityType( |
| WifiConfiguration.SECURITY_TYPE_OWE); |
| oweParams.setIsAddedByAutoUpgrade(true); |
| config.addSecurityParams(oweParams); |
| } |
| |
| private static void addPskUpgradableSecurityTypeIfNecessary(WifiConfiguration config) { |
| if (!config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)) return; |
| if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE)) return; |
| |
| Log.d(TAG, "Add upgradable SAE configuration."); |
| SecurityParams saeParams = SecurityParams.createSecurityParamsBySecurityType( |
| WifiConfiguration.SECURITY_TYPE_SAE); |
| saeParams.setIsAddedByAutoUpgrade(true); |
| config.addSecurityParams(saeParams); |
| } |
| |
| private static void addEapUpgradableSecurityTypeIfNecessary(WifiConfiguration config) { |
| if (!config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP)) return; |
| if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE)) return; |
| |
| Log.d(TAG, "Add upgradable Enterprise configuration."); |
| SecurityParams wpa3EnterpriseParams = SecurityParams.createSecurityParamsBySecurityType( |
| WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE); |
| wpa3EnterpriseParams.setIsAddedByAutoUpgrade(true); |
| config.addSecurityParams(wpa3EnterpriseParams); |
| } |
| |
| /** |
| * Add upgradable securit type to the given wifi configuration. |
| * |
| * @param config the wifi configuration to be checked. |
| */ |
| public static boolean addUpgradableSecurityTypeIfNecessary(WifiConfiguration config) { |
| try { |
| addOpenUpgradableSecurityTypeIfNecessary(config); |
| addPskUpgradableSecurityTypeIfNecessary(config); |
| addEapUpgradableSecurityTypeIfNecessary(config); |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "Failed to add upgradable security type"); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * For a upgradable type which is added by the auto-upgrade mechenism, it is only |
| * matched when corresponding auto-upgrade features are enabled. |
| */ |
| private static boolean shouldOmitAutoUpgradeParams(SecurityParams params) { |
| if (!params.isAddedByAutoUpgrade()) return false; |
| |
| WifiGlobals wifiGlobals = WifiInjector.getInstance().getWifiGlobals(); |
| |
| if (params.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE)) { |
| return !wifiGlobals.isWpa3SaeUpgradeEnabled(); |
| } |
| if (params.isSecurityType(WifiConfiguration.SECURITY_TYPE_OWE)) { |
| return !wifiGlobals.isOweUpgradeEnabled(); |
| } |
| return false; |
| } |
| |
| private static boolean isSecurityParamsSupported(SecurityParams params) { |
| final long wifiFeatures = WifiInjector.getInstance() |
| .getActiveModeWarden().getPrimaryClientModeManager() |
| .getSupportedFeatures(); |
| switch (params.getSecurityType()) { |
| case WifiConfiguration.SECURITY_TYPE_SAE: |
| return 0 != (wifiFeatures & WifiManager.WIFI_FEATURE_WPA3_SAE); |
| case WifiConfiguration.SECURITY_TYPE_OWE: |
| return 0 != (wifiFeatures & WifiManager.WIFI_FEATURE_OWE); |
| } |
| return true; |
| } |
| |
| /** |
| * Check the security params is valid or not. |
| * @param params the requesting security params. |
| * @return true if it's valid; otherwise false. |
| */ |
| public static boolean isSecurityParamsValid(SecurityParams params) { |
| if (!params.isEnabled()) return false; |
| if (!isSecurityParamsSupported(params)) return false; |
| return true; |
| } |
| |
| /** |
| * General WifiConfiguration list sorting algorithm: |
| * 1, Place the fully enabled networks first. |
| * 2. Next place all the temporarily disabled networks. |
| * 3. Place the permanently disabled networks last (Permanently disabled networks are removed |
| * before WifiConfigManager uses this comparator today!). |
| * |
| * Among the networks with the same status, sort them in the order determined by the return of |
| * {@link #compareNetworksWithSameStatus(WifiConfiguration, WifiConfiguration)} method |
| * implementation. |
| */ |
| public abstract static class WifiConfigurationComparator implements |
| Comparator<WifiConfiguration> { |
| private static final int ENABLED_NETWORK_SCORE = 3; |
| private static final int TEMPORARY_DISABLED_NETWORK_SCORE = 2; |
| private static final int PERMANENTLY_DISABLED_NETWORK_SCORE = 1; |
| |
| @Override |
| public int compare(WifiConfiguration a, WifiConfiguration b) { |
| int configAScore = getNetworkStatusScore(a); |
| int configBScore = getNetworkStatusScore(b); |
| if (configAScore == configBScore) { |
| return compareNetworksWithSameStatus(a, b); |
| } else { |
| return Integer.compare(configBScore, configAScore); |
| } |
| } |
| |
| // This needs to be implemented by the connected/disconnected PNO list comparator. |
| abstract int compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b); |
| |
| /** |
| * Returns an integer representing a score for each configuration. The scores are assigned |
| * based on the status of the configuration. The scores are assigned according to the order: |
| * Fully enabled network > Temporarily disabled network > Permanently disabled network. |
| */ |
| private int getNetworkStatusScore(WifiConfiguration config) { |
| if (config.getNetworkSelectionStatus().isNetworkEnabled()) { |
| return ENABLED_NETWORK_SCORE; |
| } else if (config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) { |
| return TEMPORARY_DISABLED_NETWORK_SCORE; |
| } else { |
| return PERMANENTLY_DISABLED_NETWORK_SCORE; |
| } |
| } |
| } |
| |
| /** |
| * Convert multi-type configurations to a list of configurations with a single security type, |
| * where a configuration with multiple security configurations will be converted to multiple |
| * Wi-Fi configurations with a single security type.. |
| * |
| * @param configs the list of multi-type configurations. |
| * @return a list of Wi-Fi configurations with a single security type, |
| * that may contain multiple configurations with the same network ID. |
| */ |
| public static List<WifiConfiguration> convertMultiTypeConfigsToLegacyConfigs( |
| List<WifiConfiguration> configs) { |
| List<WifiConfiguration> legacyConfigs = new ArrayList<>(); |
| for (WifiConfiguration config : configs) { |
| boolean wpa2EnterpriseAdded = false; |
| WifiConfiguration wpa3EnterpriseConfig = null; |
| for (SecurityParams params: config.getSecurityParamsList()) { |
| if (!params.isEnabled()) continue; |
| if (shouldOmitAutoUpgradeParams(params)) continue; |
| WifiConfiguration legacyConfig = new WifiConfiguration(config); |
| legacyConfig.setSecurityParams(params); |
| legacyConfig.networkId = addSecurityTypeToNetworkId( |
| legacyConfig.networkId, |
| params.getSecurityType()); |
| int securityType = params.getSecurityType(); |
| if (securityType == SECURITY_TYPE_EAP) { |
| wpa2EnterpriseAdded = true; |
| } else if (securityType == SECURITY_TYPE_EAP_WPA3_ENTERPRISE) { |
| wpa3EnterpriseConfig = legacyConfig; |
| continue; |
| } |
| legacyConfigs.add(legacyConfig); |
| } |
| if (wpa3EnterpriseConfig != null && (SdkLevel.isAtLeastS() || !wpa2EnterpriseAdded)) { |
| // R Wifi settings maps WPA3-Enterprise to the same security type as |
| // WPA2-Enterprise, which causes a DuplicateKeyException. For R, we should only |
| // return the WPA2-Enterprise config if we have both. |
| legacyConfigs.add(wpa3EnterpriseConfig); |
| } |
| } |
| return legacyConfigs; |
| } |
| |
| /** |
| * Converts WifiInfo.SecurityType to WifiConfiguration.SecurityType |
| */ |
| public static |
| @WifiConfiguration.SecurityType int convertWifiInfoSecurityTypeToWifiConfiguration( |
| @WifiInfo.SecurityType int securityType) { |
| switch (securityType) { |
| case WifiInfo.SECURITY_TYPE_OPEN: |
| return WifiConfiguration.SECURITY_TYPE_OPEN; |
| case WifiInfo.SECURITY_TYPE_WEP: |
| return WifiConfiguration.SECURITY_TYPE_WEP; |
| case WifiInfo.SECURITY_TYPE_PSK: |
| return WifiConfiguration.SECURITY_TYPE_PSK; |
| case WifiInfo.SECURITY_TYPE_EAP: |
| return WifiConfiguration.SECURITY_TYPE_EAP; |
| case WifiInfo.SECURITY_TYPE_SAE: |
| return WifiConfiguration.SECURITY_TYPE_SAE; |
| case WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT: |
| return WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT; |
| case WifiInfo.SECURITY_TYPE_OWE: |
| return WifiConfiguration.SECURITY_TYPE_OWE; |
| case WifiInfo.SECURITY_TYPE_WAPI_PSK: |
| return WifiConfiguration.SECURITY_TYPE_WAPI_PSK; |
| case WifiInfo.SECURITY_TYPE_WAPI_CERT: |
| return WifiConfiguration.SECURITY_TYPE_WAPI_CERT; |
| case WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE: |
| return WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE; |
| case WifiInfo.SECURITY_TYPE_OSEN: |
| return WifiConfiguration.SECURITY_TYPE_OSEN; |
| case WifiInfo.SECURITY_TYPE_PASSPOINT_R1_R2: |
| return WifiConfiguration.SECURITY_TYPE_PASSPOINT_R1_R2; |
| case WifiInfo.SECURITY_TYPE_PASSPOINT_R3: |
| return WifiConfiguration.SECURITY_TYPE_PASSPOINT_R3; |
| default: |
| return -1; |
| } |
| } |
| |
| /** |
| * Adds a WifiConfiguration.SecurityType value to a network ID to differentiate the network IDs |
| * of legacy single-type configurations derived from the same multi-type configuration. |
| * |
| * This method only works for SDK levels less than S, since those callers may expect a unique |
| * network ID for each single-type configuration. For SDK level S and above, this method returns |
| * the network ID as-is. |
| * |
| * @param netId network id to add the security type to |
| * @param securityType WifiConfiguration security type to encode |
| * @return network id with security type encoded in it |
| */ |
| public static int addSecurityTypeToNetworkId( |
| int netId, @WifiConfiguration.SecurityType int securityType) { |
| if (netId == INVALID_NETWORK_ID || SdkLevel.isAtLeastS()) { |
| return netId; |
| } |
| return removeSecurityTypeFromNetworkId(netId) |
| | ((securityType & NETWORK_ID_SECURITY_MASK) << NETWORK_ID_SECURITY_OFFSET); |
| } |
| |
| /** |
| * Removes the security type value of a network ID to have it match with internal network IDs. |
| * |
| * This method only works for SDK levels less than S. It should be used on network ID passed in |
| * from external callers, since those callers are be exposed to network IDs with an embedded |
| * security type value. For SDK levels S and above, this method returns the network ID as-is. |
| * |
| * @param netId network id to remove the security type from |
| * @return network id with the security type removed |
| */ |
| public static int removeSecurityTypeFromNetworkId(int netId) { |
| if (netId == INVALID_NETWORK_ID || SdkLevel.isAtLeastS()) { |
| return netId; |
| } |
| return netId & ~(NETWORK_ID_SECURITY_MASK << NETWORK_ID_SECURITY_OFFSET); |
| } |
| |
| private static boolean validateEnterpriseConfig(WifiConfiguration config, boolean isAdd) { |
| if ((config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP) |
| || config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE)) |
| && !config.isEnterprise()) { |
| return false; |
| } |
| if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT) |
| && (!config.isEnterprise() |
| || config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.TLS)) { |
| return false; |
| } |
| if (config.isEnterprise()) { |
| if (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP |
| || config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS) { |
| |
| int phase2Method = config.enterpriseConfig.getPhase2Method(); |
| if (phase2Method == WifiEnterpriseConfig.Phase2.MSCHAP |
| || phase2Method == WifiEnterpriseConfig.Phase2.MSCHAPV2 |
| || phase2Method == WifiEnterpriseConfig.Phase2.PAP |
| || phase2Method == WifiEnterpriseConfig.Phase2.GTC) { |
| // Check the password on add only. When updating, the password may not be |
| // available and it appears as "(Unchanged)" in Settings |
| if ((isAdd && TextUtils.isEmpty(config.enterpriseConfig.getPassword())) |
| || TextUtils.isEmpty(config.enterpriseConfig.getIdentity())) { |
| Log.e(TAG, "Enterprise network without an identity or a password set"); |
| return false; |
| } |
| } |
| } |
| } |
| return true; |
| } |
| } |