1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi; 18 19 import static android.net.wifi.WifiManager.ALL_ZEROS_MAC_ADDRESS; 20 21 import static com.android.server.wifi.util.NativeUtil.addEnclosingQuotes; 22 23 import android.annotation.SuppressLint; 24 import android.net.IpConfiguration; 25 import android.net.MacAddress; 26 import android.net.StaticIpConfiguration; 27 import android.net.wifi.SecurityParams; 28 import android.net.wifi.WifiConfiguration; 29 import android.net.wifi.WifiEnterpriseConfig; 30 import android.net.wifi.WifiManager; 31 import android.net.wifi.WifiNetworkSpecifier; 32 import android.net.wifi.WifiScanner; 33 import android.os.PatternMatcher; 34 import android.text.TextUtils; 35 import android.util.Log; 36 import android.util.Pair; 37 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.modules.utils.build.SdkLevel; 40 import com.android.server.wifi.util.NativeUtil; 41 42 import java.nio.charset.StandardCharsets; 43 import java.security.cert.X509Certificate; 44 import java.util.ArrayList; 45 import java.util.Arrays; 46 import java.util.BitSet; 47 import java.util.Comparator; 48 import java.util.List; 49 import java.util.Objects; 50 51 /** 52 * WifiConfiguration utility for any {@link android.net.wifi.WifiConfiguration} related operations. 53 * Currently contains: 54 * > Helper method to check if the WifiConfiguration object is visible to the provided users. 55 * > Helper methods to identify the encryption of a WifiConfiguration object. 56 */ 57 public class WifiConfigurationUtil { 58 private static final String TAG = "WifiConfigurationUtil"; 59 60 /** 61 * Constants used for validating external config objects. 62 */ 63 private static final int ENCLOSING_QUOTES_LEN = 2; 64 private static final int SSID_UTF_8_MIN_LEN = 1 + ENCLOSING_QUOTES_LEN; 65 private static final int SSID_UTF_8_MAX_LEN = 32 + ENCLOSING_QUOTES_LEN; 66 private static final int SSID_HEX_MIN_LEN = 2; 67 private static final int SSID_HEX_MAX_LEN = 64; 68 private static final int PSK_ASCII_MIN_LEN = 8 + ENCLOSING_QUOTES_LEN; 69 private static final int SAE_ASCII_MIN_LEN = 1 + ENCLOSING_QUOTES_LEN; 70 private static final int PSK_SAE_ASCII_MAX_LEN = 63 + ENCLOSING_QUOTES_LEN; 71 private static final int PSK_SAE_HEX_LEN = 64; 72 private static final int WEP104_KEY_BYTES_LEN = 13; 73 private static final int WEP40_KEY_BYTES_LEN = 5; 74 75 @VisibleForTesting 76 public static final String PASSWORD_MASK = "*"; 77 private static final String MATCH_EMPTY_SSID_PATTERN_PATH = ""; 78 private static final Pair<MacAddress, MacAddress> MATCH_NONE_BSSID_PATTERN = 79 new Pair<>(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS); 80 private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN = 81 new Pair<>(ALL_ZEROS_MAC_ADDRESS, ALL_ZEROS_MAC_ADDRESS); 82 83 /** 84 * Checks if the provided |wepKeys| array contains any non-null value; 85 */ hasAnyValidWepKey(String[] wepKeys)86 public static boolean hasAnyValidWepKey(String[] wepKeys) { 87 for (int i = 0; i < wepKeys.length; i++) { 88 if (wepKeys[i] != null) { 89 return true; 90 } 91 } 92 return false; 93 } 94 95 /** 96 * Helper method to check if the provided |config| corresponds to a PSK network or not. 97 */ isConfigForPskNetwork(WifiConfiguration config)98 public static boolean isConfigForPskNetwork(WifiConfiguration config) { 99 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK); 100 } 101 102 /** 103 * Helper method to check if the provided |config| corresponds to a WAPI PSK network or not. 104 */ isConfigForWapiPskNetwork(WifiConfiguration config)105 public static boolean isConfigForWapiPskNetwork(WifiConfiguration config) { 106 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WAPI_PSK); 107 } 108 109 /** 110 * Helper method to check if the provided |config| corresponds to a WAPI CERT network or not. 111 */ isConfigForWapiCertNetwork(WifiConfiguration config)112 public static boolean isConfigForWapiCertNetwork(WifiConfiguration config) { 113 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WAPI_CERT); 114 } 115 116 /** 117 * Helper method to check if the provided |config| corresponds to an SAE network or not. 118 */ isConfigForSaeNetwork(WifiConfiguration config)119 public static boolean isConfigForSaeNetwork(WifiConfiguration config) { 120 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE); 121 } 122 123 /** 124 * Helper method to check if the provided |config| corresponds to an OWE network or not. 125 */ isConfigForOweNetwork(WifiConfiguration config)126 public static boolean isConfigForOweNetwork(WifiConfiguration config) { 127 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_OWE); 128 } 129 130 /** 131 * Helper method to check if the provided |config| corresponds to a EAP network or not. 132 */ isConfigForEapNetwork(WifiConfiguration config)133 public static boolean isConfigForEapNetwork(WifiConfiguration config) { 134 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP); 135 } 136 137 /** 138 * Helper method to check if the provided |config| corresponds to 139 * a WPA3 Enterprise network or not. 140 */ isConfigForWpa3EnterpriseNetwork(WifiConfiguration config)141 public static boolean isConfigForWpa3EnterpriseNetwork(WifiConfiguration config) { 142 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE); 143 } 144 145 /** 146 * Helper method to check if the provided |config| corresponds to a EAP Suite-B network or not. 147 */ isConfigForWpa3Enterprise192BitNetwork(WifiConfiguration config)148 public static boolean isConfigForWpa3Enterprise192BitNetwork(WifiConfiguration config) { 149 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT); 150 } 151 152 /** 153 * Helper method to check if the provided |config| corresponds to a WEP network or not. 154 */ isConfigForWepNetwork(WifiConfiguration config)155 public static boolean isConfigForWepNetwork(WifiConfiguration config) { 156 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WEP); 157 } 158 159 /** 160 * Helper method to check if the provided |config| corresponds to a Passpoint network or not. 161 */ isConfigForPasspoint(WifiConfiguration config)162 public static boolean isConfigForPasspoint(WifiConfiguration config) { 163 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PASSPOINT_R1_R2) 164 || config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PASSPOINT_R3); 165 } 166 167 /** 168 * Helper method to check if the provided |config| corresponds to an open or enhanced 169 * open network, or not. 170 */ isConfigForOpenNetwork(WifiConfiguration config)171 public static boolean isConfigForOpenNetwork(WifiConfiguration config) { 172 return (!(isConfigForWepNetwork(config) || isConfigForPskNetwork(config) 173 || isConfigForWapiPskNetwork(config) || isConfigForWapiCertNetwork(config) 174 || isConfigForEapNetwork(config) || isConfigForSaeNetwork(config) 175 || isConfigForWpa3Enterprise192BitNetwork(config) 176 || isConfigForPasspoint(config))); 177 } 178 179 /** 180 * Compare existing and new WifiConfiguration objects after a network update and return if 181 * IP parameters have changed or not. 182 * 183 * @param existingConfig Existing WifiConfiguration object corresponding to the network. 184 * @param newConfig New WifiConfiguration object corresponding to the network. 185 * @return true if IP parameters have changed, false otherwise. 186 */ hasIpChanged(WifiConfiguration existingConfig, WifiConfiguration newConfig)187 public static boolean hasIpChanged(WifiConfiguration existingConfig, 188 WifiConfiguration newConfig) { 189 if (existingConfig.getIpAssignment() != newConfig.getIpAssignment()) { 190 return true; 191 } 192 if (newConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC) { 193 return !Objects.equals(existingConfig.getStaticIpConfiguration(), 194 newConfig.getStaticIpConfiguration()); 195 } 196 return false; 197 } 198 199 /** 200 * Compare existing and new WifiConfiguration objects after a network update and return if 201 * proxy parameters have changed or not. 202 * 203 * @param existingConfig Existing WifiConfiguration object corresponding to the network. 204 * @param newConfig New WifiConfiguration object corresponding to the network. 205 * @return true if proxy parameters have changed, false if no existing config and proxy settings 206 * are NONE, false otherwise. 207 */ hasProxyChanged(WifiConfiguration existingConfig, WifiConfiguration newConfig)208 public static boolean hasProxyChanged(WifiConfiguration existingConfig, 209 WifiConfiguration newConfig) { 210 if (existingConfig == null) { 211 return newConfig.getProxySettings() != IpConfiguration.ProxySettings.NONE; 212 } 213 if (newConfig.getProxySettings() != existingConfig.getProxySettings()) { 214 return true; 215 } 216 return !Objects.equals(existingConfig.getHttpProxy(), newConfig.getHttpProxy()); 217 } 218 219 /** 220 * Compare existing and new WifiConfiguration objects after a network update and return if 221 * Repeater Enabled flag has changed or not. In case the there is no existing WifiConfiguration, 222 * checks if Repeater Enabled flag has changed from the default value of false. 223 * 224 * @param existingConfig Existing WifiConfiguration object corresponding to the network. 225 * @param newConfig New WifiConfiguration object corresponding to the network. 226 * @return true if RepeaterEnabled flag has changed, or if there is no existing config, and 227 * the flag is set to true, false otherwise. 228 */ hasRepeaterEnabledChanged(WifiConfiguration existingConfig, WifiConfiguration newConfig)229 public static boolean hasRepeaterEnabledChanged(WifiConfiguration existingConfig, 230 WifiConfiguration newConfig) { 231 if (existingConfig == null) { 232 return newConfig.isRepeaterEnabled(); 233 } 234 return (newConfig.isRepeaterEnabled() != existingConfig.isRepeaterEnabled()); 235 } 236 237 /** 238 * Compare existing and new WifiConfiguration objects after a network update and return if 239 * MAC randomization setting has changed or not. 240 * @param existingConfig Existing WifiConfiguration object corresponding to the network. 241 * @param newConfig New WifiConfiguration object corresponding to the network. 242 * @return true if MAC randomization setting setting changed or the existing confiuration is 243 * null and the newConfig is setting macRandomizationSetting to the default value. 244 */ hasMacRandomizationSettingsChanged(WifiConfiguration existingConfig, WifiConfiguration newConfig)245 public static boolean hasMacRandomizationSettingsChanged(WifiConfiguration existingConfig, 246 WifiConfiguration newConfig) { 247 if (existingConfig == null) { 248 return newConfig.macRandomizationSetting != WifiConfiguration.RANDOMIZATION_AUTO; 249 } 250 return newConfig.macRandomizationSetting != existingConfig.macRandomizationSetting; 251 } 252 253 /** 254 * Compare existing and new WifiEnterpriseConfig objects after a network update and return if 255 * credential parameters have changed or not. 256 * 257 * @param existingEnterpriseConfig Existing WifiConfiguration object corresponding to the 258 * network. 259 * @param newEnterpriseConfig New WifiConfiguration object corresponding to the network. 260 * @return true if credentials have changed, false otherwise. 261 */ 262 @VisibleForTesting hasEnterpriseConfigChanged(WifiEnterpriseConfig existingEnterpriseConfig, WifiEnterpriseConfig newEnterpriseConfig)263 public static boolean hasEnterpriseConfigChanged(WifiEnterpriseConfig existingEnterpriseConfig, 264 WifiEnterpriseConfig newEnterpriseConfig) { 265 if (existingEnterpriseConfig != null && newEnterpriseConfig != null) { 266 if (existingEnterpriseConfig.getEapMethod() != newEnterpriseConfig.getEapMethod()) { 267 return true; 268 } 269 if (existingEnterpriseConfig.isAuthenticationSimBased()) { 270 // The anonymous identity will be decorated with 3gpp realm in the service. 271 if (!TextUtils.equals(existingEnterpriseConfig.getAnonymousIdentity(), 272 newEnterpriseConfig.getAnonymousIdentity())) { 273 return true; 274 } 275 return false; 276 } 277 if (existingEnterpriseConfig.getPhase2Method() 278 != newEnterpriseConfig.getPhase2Method()) { 279 return true; 280 } 281 if (!TextUtils.equals(existingEnterpriseConfig.getIdentity(), 282 newEnterpriseConfig.getIdentity())) { 283 return true; 284 } 285 if (!TextUtils.equals(existingEnterpriseConfig.getAnonymousIdentity(), 286 newEnterpriseConfig.getAnonymousIdentity())) { 287 return true; 288 } 289 if (!TextUtils.equals(existingEnterpriseConfig.getPassword(), 290 newEnterpriseConfig.getPassword())) { 291 return true; 292 } 293 X509Certificate[] existingCaCerts = existingEnterpriseConfig.getCaCertificates(); 294 X509Certificate[] newCaCerts = newEnterpriseConfig.getCaCertificates(); 295 if (!Arrays.equals(existingCaCerts, newCaCerts)) { 296 return true; 297 } 298 if (!Arrays.equals(newEnterpriseConfig.getCaCertificateAliases(), 299 existingEnterpriseConfig.getCaCertificateAliases())) { 300 return true; 301 } 302 if (!TextUtils.equals(newEnterpriseConfig.getClientCertificateAlias(), 303 existingEnterpriseConfig.getClientCertificateAlias())) { 304 return true; 305 } 306 if (!TextUtils.equals(newEnterpriseConfig.getAltSubjectMatch(), 307 existingEnterpriseConfig.getAltSubjectMatch())) { 308 return true; 309 } 310 if (!TextUtils.equals(newEnterpriseConfig.getWapiCertSuite(), 311 existingEnterpriseConfig.getWapiCertSuite())) { 312 return true; 313 } 314 if (newEnterpriseConfig.getOcsp() != existingEnterpriseConfig.getOcsp()) { 315 return true; 316 } 317 } else { 318 // One of the configs may have an enterpriseConfig 319 if (existingEnterpriseConfig != null || newEnterpriseConfig != null) { 320 return true; 321 } 322 } 323 return false; 324 } 325 326 /** 327 * Compare existing and new WifiConfiguration objects after a network update and return if 328 * credential parameters have changed or not. 329 * 330 * @param existingConfig Existing WifiConfiguration object corresponding to the network. 331 * @param newConfig New WifiConfiguration object corresponding to the network. 332 * @return true if credentials have changed, false otherwise. 333 */ hasCredentialChanged(WifiConfiguration existingConfig, WifiConfiguration newConfig)334 public static boolean hasCredentialChanged(WifiConfiguration existingConfig, 335 WifiConfiguration newConfig) { 336 if (!Objects.equals(existingConfig.allowedKeyManagement, 337 newConfig.allowedKeyManagement)) { 338 return true; 339 } 340 if (!Objects.equals(existingConfig.allowedProtocols, newConfig.allowedProtocols)) { 341 return true; 342 } 343 if (!Objects.equals(existingConfig.allowedAuthAlgorithms, 344 newConfig.allowedAuthAlgorithms)) { 345 return true; 346 } 347 if (!Objects.equals(existingConfig.allowedPairwiseCiphers, 348 newConfig.allowedPairwiseCiphers)) { 349 return true; 350 } 351 if (!Objects.equals(existingConfig.allowedGroupCiphers, 352 newConfig.allowedGroupCiphers)) { 353 return true; 354 } 355 if (!Objects.equals(existingConfig.allowedGroupManagementCiphers, 356 newConfig.allowedGroupManagementCiphers)) { 357 return true; 358 } 359 if (!Objects.equals(existingConfig.allowedSuiteBCiphers, 360 newConfig.allowedSuiteBCiphers)) { 361 return true; 362 } 363 if (!existingConfig.getSecurityParamsList().equals(newConfig.getSecurityParamsList())) { 364 return true; 365 } 366 if (!Objects.equals(existingConfig.preSharedKey, newConfig.preSharedKey)) { 367 return true; 368 } 369 if (!Arrays.equals(existingConfig.wepKeys, newConfig.wepKeys)) { 370 return true; 371 } 372 if (existingConfig.wepTxKeyIndex != newConfig.wepTxKeyIndex) { 373 return true; 374 } 375 if (existingConfig.hiddenSSID != newConfig.hiddenSSID) { 376 return true; 377 } 378 if (existingConfig.requirePmf != newConfig.requirePmf) { 379 return true; 380 } 381 if (existingConfig.carrierId != newConfig.carrierId) { 382 return true; 383 } 384 if (hasEnterpriseConfigChanged(existingConfig.enterpriseConfig, 385 newConfig.enterpriseConfig)) { 386 return true; 387 } 388 return false; 389 } 390 validateSsid(String ssid, boolean isAdd)391 private static boolean validateSsid(String ssid, boolean isAdd) { 392 if (isAdd) { 393 if (ssid == null) { 394 Log.e(TAG, "validateSsid : null string"); 395 return false; 396 } 397 } else { 398 if (ssid == null) { 399 // This is an update, so the SSID can be null if that is not being changed. 400 return true; 401 } 402 } 403 if (ssid.isEmpty()) { 404 Log.e(TAG, "validateSsid failed: empty string"); 405 return false; 406 } 407 if (ssid.startsWith("\"")) { 408 // UTF-8 SSID string 409 byte[] ssidBytes = ssid.getBytes(StandardCharsets.UTF_8); 410 if (ssidBytes.length < SSID_UTF_8_MIN_LEN) { 411 Log.e(TAG, "validateSsid failed: utf-8 ssid string size too small: " 412 + ssidBytes.length); 413 return false; 414 } 415 if (ssidBytes.length > SSID_UTF_8_MAX_LEN) { 416 Log.e(TAG, "validateSsid failed: utf-8 ssid string size too large: " 417 + ssidBytes.length); 418 return false; 419 } 420 } else { 421 // HEX SSID string 422 if (ssid.length() < SSID_HEX_MIN_LEN) { 423 Log.e(TAG, "validateSsid failed: hex string size too small: " + ssid.length()); 424 return false; 425 } 426 if (ssid.length() > SSID_HEX_MAX_LEN) { 427 Log.e(TAG, "validateSsid failed: hex string size too large: " + ssid.length()); 428 return false; 429 } 430 } 431 try { 432 NativeUtil.decodeSsid(ssid); 433 } catch (IllegalArgumentException e) { 434 Log.e(TAG, "validateSsid failed: malformed string: " + ssid); 435 return false; 436 } 437 return true; 438 } 439 validateBssid(MacAddress bssid)440 private static boolean validateBssid(MacAddress bssid) { 441 if (bssid == null) return true; 442 if (bssid.getAddressType() != MacAddress.TYPE_UNICAST) { 443 Log.e(TAG, "validateBssid failed: invalid bssid"); 444 return false; 445 } 446 return true; 447 } 448 validateBssid(String bssid)449 private static boolean validateBssid(String bssid) { 450 if (bssid == null) return true; 451 if (bssid.isEmpty()) { 452 Log.e(TAG, "validateBssid failed: empty string"); 453 return false; 454 } 455 // Allow reset of bssid with "any". 456 if (bssid.equals(ClientModeImpl.SUPPLICANT_BSSID_ANY)) return true; 457 MacAddress bssidMacAddress; 458 try { 459 bssidMacAddress = MacAddress.fromString(bssid); 460 } catch (IllegalArgumentException e) { 461 Log.e(TAG, "validateBssid failed: malformed string: " + bssid); 462 return false; 463 } 464 if (!validateBssid(bssidMacAddress)) { 465 return false; 466 } 467 return true; 468 } 469 validatePassword(String password, boolean isAdd, boolean isSae)470 private static boolean validatePassword(String password, boolean isAdd, boolean isSae) { 471 if (isAdd) { 472 if (password == null) { 473 Log.e(TAG, "validatePassword: null string"); 474 return false; 475 } 476 } else { 477 if (password == null) { 478 // This is an update, so the psk can be null if that is not being changed. 479 return true; 480 } else if (password.equals(PASSWORD_MASK)) { 481 // This is an update, so the app might have returned back the masked password, let 482 // it thru. WifiConfigManager will handle it. 483 return true; 484 } 485 } 486 if (password.isEmpty()) { 487 Log.e(TAG, "validatePassword failed: empty string"); 488 return false; 489 } 490 if (password.startsWith("\"")) { 491 // ASCII PSK string 492 byte[] passwordBytes = password.getBytes(StandardCharsets.US_ASCII); 493 int targetMinLength; 494 495 if (isSae) { 496 targetMinLength = SAE_ASCII_MIN_LEN; 497 } else { 498 targetMinLength = PSK_ASCII_MIN_LEN; 499 } 500 if (passwordBytes.length < targetMinLength) { 501 Log.e(TAG, "validatePassword failed: ASCII string size too small: " 502 + passwordBytes.length); 503 return false; 504 } 505 if (passwordBytes.length > PSK_SAE_ASCII_MAX_LEN) { 506 Log.e(TAG, "validatePassword failed: ASCII string size too large: " 507 + passwordBytes.length); 508 return false; 509 } 510 } else { 511 // HEX PSK string 512 if (password.length() != PSK_SAE_HEX_LEN) { 513 Log.e(TAG, "validatePassword failed: hex string size mismatch: " 514 + password.length()); 515 return false; 516 } 517 } 518 try { 519 NativeUtil.hexOrQuotedStringToBytes(password); 520 } catch (IllegalArgumentException e) { 521 Log.e(TAG, "validatePassword failed: malformed string: " + password); 522 return false; 523 } 524 return true; 525 } 526 validateWepKeys(String[] wepKeys, int wepTxKeyIndex, boolean isAdd)527 private static boolean validateWepKeys(String[] wepKeys, int wepTxKeyIndex, boolean isAdd) { 528 if (isAdd) { 529 if (wepKeys == null) { 530 Log.e(TAG, "validateWepKeys: null string"); 531 return false; 532 } 533 } else { 534 if (wepKeys == null) { 535 // This is an update, so the psk can be null if that is not being changed. 536 return true; 537 } else { 538 boolean allMaskedKeys = true; 539 for (int i = 0; i < wepKeys.length; i++) { 540 if (wepKeys[i] != null && !TextUtils.equals(wepKeys[i], PASSWORD_MASK)) { 541 allMaskedKeys = false; 542 } 543 } 544 if (allMaskedKeys) { 545 // This is an update, so the app might have returned back the masked password, 546 // let it thru. WifiConfigManager will handle it. 547 return true; 548 } 549 } 550 } 551 for (int i = 0; i < wepKeys.length; i++) { 552 if (wepKeys[i] != null) { 553 try { 554 ArrayList<Byte> wepKeyBytes = 555 NativeUtil.hexOrQuotedStringToBytes(wepKeys[i]); 556 if (wepKeyBytes.size() != WEP40_KEY_BYTES_LEN 557 && wepKeyBytes.size() != WEP104_KEY_BYTES_LEN) { 558 Log.e(TAG, "validateWepKeys: invalid wep key length " 559 + wepKeys[i].length() + " at index " + i); 560 return false; 561 } 562 } catch (IllegalArgumentException e) { 563 Log.e(TAG, "validateWepKeys: invalid wep key at index " + i, e); 564 return false; 565 } 566 } 567 } 568 if (wepTxKeyIndex >= wepKeys.length) { 569 Log.e(TAG, "validateWepKeys: invalid wep tx key index " + wepTxKeyIndex 570 + " wepKeys len: " + wepKeys.length); 571 return false; 572 } 573 return true; 574 } 575 validateBitSet(BitSet bitSet, int validValuesLength)576 private static boolean validateBitSet(BitSet bitSet, int validValuesLength) { 577 if (bitSet == null) return false; 578 BitSet clonedBitset = (BitSet) bitSet.clone(); 579 clonedBitset.clear(0, validValuesLength); 580 return clonedBitset.isEmpty(); 581 } 582 validateBitSets(WifiConfiguration config)583 private static boolean validateBitSets(WifiConfiguration config) { 584 // 1. Check |allowedKeyManagement|. 585 if (!validateBitSet(config.allowedKeyManagement, 586 WifiConfiguration.KeyMgmt.strings.length)) { 587 Log.e(TAG, "validateBitsets failed: invalid allowedKeyManagement bitset " 588 + config.allowedKeyManagement); 589 return false; 590 } 591 // 2. Check |allowedProtocols|. 592 if (!validateBitSet(config.allowedProtocols, 593 WifiConfiguration.Protocol.strings.length)) { 594 Log.e(TAG, "validateBitsets failed: invalid allowedProtocols bitset " 595 + config.allowedProtocols); 596 return false; 597 } 598 // 3. Check |allowedAuthAlgorithms|. 599 if (!validateBitSet(config.allowedAuthAlgorithms, 600 WifiConfiguration.AuthAlgorithm.strings.length)) { 601 Log.e(TAG, "validateBitsets failed: invalid allowedAuthAlgorithms bitset " 602 + config.allowedAuthAlgorithms); 603 return false; 604 } 605 // 4. Check |allowedGroupCiphers|. 606 if (!validateBitSet(config.allowedGroupCiphers, 607 WifiConfiguration.GroupCipher.strings.length)) { 608 Log.e(TAG, "validateBitsets failed: invalid allowedGroupCiphers bitset " 609 + config.allowedGroupCiphers); 610 return false; 611 } 612 // 5. Check |allowedPairwiseCiphers|. 613 if (!validateBitSet(config.allowedPairwiseCiphers, 614 WifiConfiguration.PairwiseCipher.strings.length)) { 615 Log.e(TAG, "validateBitsets failed: invalid allowedPairwiseCiphers bitset " 616 + config.allowedPairwiseCiphers); 617 return false; 618 } 619 return true; 620 } 621 validateKeyMgmt(BitSet keyMgmnt)622 private static boolean validateKeyMgmt(BitSet keyMgmnt) { 623 if (keyMgmnt.cardinality() > 1) { 624 if (keyMgmnt.cardinality() > 3) { 625 Log.e(TAG, "validateKeyMgmt failed: cardinality > 3"); 626 return false; 627 } 628 if (!keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_EAP)) { 629 Log.e(TAG, "validateKeyMgmt failed: not WPA_EAP"); 630 return false; 631 } 632 if (!keyMgmnt.get(WifiConfiguration.KeyMgmt.IEEE8021X) 633 && !keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { 634 Log.e(TAG, "validateKeyMgmt failed: not PSK or 8021X"); 635 return false; 636 } 637 // SUITE-B keymgmt must be WPA_EAP + IEEE8021X + SUITE_B_192. 638 if (keyMgmnt.cardinality() == 3 639 && !(keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_EAP) 640 && keyMgmnt.get(WifiConfiguration.KeyMgmt.IEEE8021X) 641 && keyMgmnt.get(WifiConfiguration.KeyMgmt.SUITE_B_192))) { 642 Log.e(TAG, "validateKeyMgmt failed: not SUITE_B_192"); 643 return false; 644 } 645 } 646 return true; 647 } 648 validateIpConfiguration(IpConfiguration ipConfig)649 private static boolean validateIpConfiguration(IpConfiguration ipConfig) { 650 if (ipConfig == null) { 651 Log.e(TAG, "validateIpConfiguration failed: null IpConfiguration"); 652 return false; 653 } 654 if (ipConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC) { 655 StaticIpConfiguration staticIpConfig = ipConfig.getStaticIpConfiguration(); 656 if (staticIpConfig == null) { 657 Log.e(TAG, "validateIpConfiguration failed: null StaticIpConfiguration"); 658 return false; 659 } 660 if (staticIpConfig.getIpAddress() == null) { 661 Log.e(TAG, "validateIpConfiguration failed: null static ip Address"); 662 return false; 663 } 664 } 665 return true; 666 } 667 668 /** 669 * Enums to specify if the provided config is being validated for add or update. 670 */ 671 public static final boolean VALIDATE_FOR_ADD = true; 672 public static final boolean VALIDATE_FOR_UPDATE = false; 673 674 /** 675 * Validate the configuration received from an external application. 676 * 677 * This method checks for the following parameters: 678 * 1. {@link WifiConfiguration#SSID} 679 * 2. {@link WifiConfiguration#BSSID} 680 * 3. {@link WifiConfiguration#preSharedKey} 681 * 4. {@link WifiConfiguration#allowedKeyManagement} 682 * 5. {@link WifiConfiguration#allowedProtocols} 683 * 6. {@link WifiConfiguration#allowedAuthAlgorithms} 684 * 7. {@link WifiConfiguration#allowedGroupCiphers} 685 * 8. {@link WifiConfiguration#allowedPairwiseCiphers} 686 * 9. {@link WifiConfiguration#getIpConfiguration()} 687 * 688 * @param config {@link WifiConfiguration} received from an external application. 689 * @param supportedFeatureSet bitmask for supported features using {@code WIFI_FEATURE_} 690 * @param isAdd {@link #VALIDATE_FOR_ADD} to indicate a network config received for an add, 691 * {@link #VALIDATE_FOR_UPDATE} for a network config received for an update. 692 * These 2 cases need to be handled differently because the config received for an 693 * update could contain only the fields that are being changed. 694 * @return true if the parameters are valid, false otherwise. 695 */ validate(WifiConfiguration config, long supportedFeatureSet, boolean isAdd)696 public static boolean validate(WifiConfiguration config, long supportedFeatureSet, 697 boolean isAdd) { 698 if (!validateSsid(config.SSID, isAdd)) { 699 return false; 700 } 701 if (!validateBssid(config.BSSID)) { 702 return false; 703 } 704 if (!validateBitSets(config)) { 705 return false; 706 } 707 if (!validateKeyMgmt(config.allowedKeyManagement)) { 708 return false; 709 } 710 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WEP) 711 && config.wepKeys != null 712 && !validateWepKeys(config.wepKeys, config.wepTxKeyIndex, isAdd)) { 713 return false; 714 } 715 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK) 716 && !validatePassword(config.preSharedKey, isAdd, false)) { 717 return false; 718 } 719 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE) 720 && !validatePassword(config.preSharedKey, isAdd, true)) { 721 return false; 722 } 723 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WAPI_PSK) 724 && !validatePassword(config.preSharedKey, isAdd, false)) { 725 return false; 726 } 727 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_DPP) 728 && (supportedFeatureSet & WifiManager.WIFI_FEATURE_DPP_AKM) == 0) { 729 Log.e(TAG, "DPP AKM is not supported"); 730 return false; 731 } 732 if (!validateEnterpriseConfig(config, isAdd)) { 733 return false; 734 } 735 736 // b/153435438: Added to deal with badly formed WifiConfiguration from apps. 737 if (config.preSharedKey != null && !config.needsPreSharedKey()) { 738 Log.e(TAG, "preSharedKey set with an invalid KeyMgmt, resetting KeyMgmt to WPA_PSK"); 739 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); 740 } 741 if (!validateIpConfiguration(config.getIpConfiguration())) { 742 return false; 743 } 744 // TBD: Validate some enterprise params as well in the future here. 745 return true; 746 } 747 validateBssidPattern( Pair<MacAddress, MacAddress> bssidPatternMatcher)748 private static boolean validateBssidPattern( 749 Pair<MacAddress, MacAddress> bssidPatternMatcher) { 750 if (bssidPatternMatcher == null) return true; 751 MacAddress baseAddress = bssidPatternMatcher.first; 752 MacAddress mask = bssidPatternMatcher.second; 753 if (baseAddress.getAddressType() != MacAddress.TYPE_UNICAST) { 754 Log.e(TAG, "validateBssidPatternMatcher failed : invalid base address: " + baseAddress); 755 return false; 756 } 757 if (mask.equals(ALL_ZEROS_MAC_ADDRESS) 758 && !baseAddress.equals(ALL_ZEROS_MAC_ADDRESS)) { 759 Log.e(TAG, "validateBssidPatternMatcher failed : invalid mask/base: " + mask + "/" 760 + baseAddress); 761 return false; 762 } 763 return true; 764 } 765 766 // TODO(b/113878056): Some of this is duplicated in {@link WifiNetworkConfigBuilder}. 767 // Merge them somehow?. isValidNetworkSpecifier(WifiNetworkSpecifier specifier)768 private static boolean isValidNetworkSpecifier(WifiNetworkSpecifier specifier) { 769 PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher; 770 Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher; 771 if (ssidPatternMatcher == null || bssidPatternMatcher == null) { 772 return false; 773 } 774 if (ssidPatternMatcher.getPath() == null || bssidPatternMatcher.first == null 775 || bssidPatternMatcher.second == null) { 776 return false; 777 } 778 return true; 779 } 780 isMatchNoneNetworkSpecifier(WifiNetworkSpecifier specifier)781 private static boolean isMatchNoneNetworkSpecifier(WifiNetworkSpecifier specifier) { 782 PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher; 783 Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher; 784 if (ssidPatternMatcher.getType() != PatternMatcher.PATTERN_PREFIX 785 && ssidPatternMatcher.getPath().equals(MATCH_EMPTY_SSID_PATTERN_PATH)) { 786 return true; 787 } 788 if (bssidPatternMatcher.equals(MATCH_NONE_BSSID_PATTERN)) { 789 return true; 790 } 791 return false; 792 } 793 794 /** 795 * Check if the network specifier matches all networks. 796 * @param specifier The network specifier 797 * @return true if it matches all networks. 798 */ isMatchAllNetworkSpecifier(WifiNetworkSpecifier specifier)799 public static boolean isMatchAllNetworkSpecifier(WifiNetworkSpecifier specifier) { 800 PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher; 801 Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher; 802 if (ssidPatternMatcher.match(MATCH_EMPTY_SSID_PATTERN_PATH) 803 && bssidPatternMatcher.equals(MATCH_ALL_BSSID_PATTERN)) { 804 return true; 805 } 806 return false; 807 } 808 809 // TODO: b/177434707 calls inside same module are safe 810 @SuppressLint("NewApi") getBand(WifiNetworkSpecifier s)811 private static int getBand(WifiNetworkSpecifier s) { 812 return s.getBand(); 813 } 814 815 /** 816 * Validate the configuration received from an external application inside 817 * {@link WifiNetworkSpecifier}. 818 * 819 * This method checks for the following parameters: 820 * 1. {@link WifiNetworkSpecifier#ssidPatternMatcher} 821 * 2. {@link WifiNetworkSpecifier#bssidPatternMatcher} 822 * 3. {@link WifiNetworkSpecifier#getBand()} 823 * 4. {@link WifiConfiguration#SSID} 824 * 5. {@link WifiConfiguration#BSSID} 825 * 6. {@link WifiConfiguration#preSharedKey} 826 * 7. {@link WifiConfiguration#allowedKeyManagement} 827 * 8. {@link WifiConfiguration#allowedProtocols} 828 * 9. {@link WifiConfiguration#allowedAuthAlgorithms} 829 * 10. {@link WifiConfiguration#allowedGroupCiphers} 830 * 11. {@link WifiConfiguration#allowedPairwiseCiphers} 831 * 12. {@link WifiConfiguration#getIpConfiguration()} 832 * 833 * @param specifier Instance of {@link WifiNetworkSpecifier}. 834 * @return true if the parameters are valid, false otherwise. 835 */ validateNetworkSpecifier(WifiNetworkSpecifier specifier)836 public static boolean validateNetworkSpecifier(WifiNetworkSpecifier specifier) { 837 if (!isValidNetworkSpecifier(specifier)) { 838 Log.e(TAG, "validateNetworkSpecifier failed : invalid network specifier"); 839 return false; 840 } 841 if (isMatchNoneNetworkSpecifier(specifier)) { 842 Log.e(TAG, "validateNetworkSpecifier failed : match-none specifier"); 843 return false; 844 } 845 if (isMatchAllNetworkSpecifier(specifier)) { 846 Log.e(TAG, "validateNetworkSpecifier failed : match-all specifier"); 847 return false; 848 } 849 if (!WifiNetworkSpecifier.validateBand(getBand(specifier))) { 850 return false; 851 } 852 WifiConfiguration config = specifier.wifiConfiguration; 853 if (specifier.ssidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) { 854 // For literal SSID matches, the value should satisfy SSID requirements. 855 // WifiConfiguration.SSID needs quotes around ASCII SSID. 856 if (!validateSsid(addEnclosingQuotes(specifier.ssidPatternMatcher.getPath()), true)) { 857 return false; 858 } 859 } else { 860 if (config.hiddenSSID) { 861 Log.e(TAG, "validateNetworkSpecifier failed : ssid pattern not supported " 862 + "for hidden networks"); 863 return false; 864 } 865 } 866 if (Objects.equals(specifier.bssidPatternMatcher.second, MacAddress.BROADCAST_ADDRESS)) { 867 // For literal BSSID matches, the value should satisfy MAC address requirements. 868 if (!validateBssid(specifier.bssidPatternMatcher.first)) { 869 return false; 870 } 871 } else { 872 if (!validateBssidPattern(specifier.bssidPatternMatcher)) { 873 return false; 874 } 875 } 876 if (!validateBitSets(config)) { 877 return false; 878 } 879 if (!validateKeyMgmt(config.allowedKeyManagement)) { 880 return false; 881 } 882 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK) 883 && !validatePassword(config.preSharedKey, true, false)) { 884 return false; 885 } 886 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE) 887 && !validatePassword(config.preSharedKey, true, true)) { 888 return false; 889 } 890 // TBD: Validate some enterprise params as well in the future here. 891 return true; 892 } 893 894 /** 895 * Check if the provided two networks are the same. 896 * Note: This does not check if network selection BSSID's are the same. 897 * 898 * @param config Configuration corresponding to a network. 899 * @param config1 Configuration corresponding to another network. 900 * 901 * @return true if |config| and |config1| are the same network. 902 * false otherwise. 903 */ isSameNetwork(WifiConfiguration config, WifiConfiguration config1)904 public static boolean isSameNetwork(WifiConfiguration config, WifiConfiguration config1) { 905 if (config == null && config1 == null) { 906 return true; 907 } 908 if (config == null || config1 == null) { 909 return false; 910 } 911 if (config.networkId != config1.networkId) { 912 return false; 913 } 914 if (!Objects.equals(config.SSID, config1.SSID)) { 915 return false; 916 } 917 if (!Objects.equals(config.getNetworkSelectionStatus().getCandidateSecurityParams(), 918 config1.getNetworkSelectionStatus().getCandidateSecurityParams())) { 919 return false; 920 } 921 if (WifiConfigurationUtil.hasCredentialChanged(config, config1)) { 922 return false; 923 } 924 return true; 925 } 926 927 /** 928 * Create a PnoNetwork object from the provided WifiConfiguration. 929 * 930 * @param config Configuration corresponding to the network. 931 * @return PnoNetwork object corresponding to the network. 932 */ createPnoNetwork( WifiConfiguration config)933 public static WifiScanner.PnoSettings.PnoNetwork createPnoNetwork( 934 WifiConfiguration config) { 935 WifiScanner.PnoSettings.PnoNetwork pnoNetwork = 936 new WifiScanner.PnoSettings.PnoNetwork(config.SSID); 937 if (config.hiddenSSID) { 938 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN; 939 } 940 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_A_BAND; 941 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_G_BAND; 942 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)) { 943 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_PSK; 944 } else if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP)) { 945 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_EAPOL; 946 } else { 947 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_OPEN; 948 } 949 return pnoNetwork; 950 } 951 addOpenUpgradableSecurityTypeIfNecessary(WifiConfiguration config)952 private static void addOpenUpgradableSecurityTypeIfNecessary(WifiConfiguration config) { 953 if (!config.isSecurityType(WifiConfiguration.SECURITY_TYPE_OPEN)) return; 954 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_OWE)) return; 955 956 Log.d(TAG, "Add upgradable OWE configuration."); 957 SecurityParams oweParams = SecurityParams.createSecurityParamsBySecurityType( 958 WifiConfiguration.SECURITY_TYPE_OWE); 959 oweParams.setIsAddedByAutoUpgrade(true); 960 config.addSecurityParams(oweParams); 961 } 962 addPskUpgradableSecurityTypeIfNecessary(WifiConfiguration config)963 private static void addPskUpgradableSecurityTypeIfNecessary(WifiConfiguration config) { 964 if (!config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)) return; 965 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE)) return; 966 967 Log.d(TAG, "Add upgradable SAE configuration."); 968 SecurityParams saeParams = SecurityParams.createSecurityParamsBySecurityType( 969 WifiConfiguration.SECURITY_TYPE_SAE); 970 saeParams.setIsAddedByAutoUpgrade(true); 971 config.addSecurityParams(saeParams); 972 } 973 addEapUpgradableSecurityTypeIfNecessary(WifiConfiguration config)974 private static void addEapUpgradableSecurityTypeIfNecessary(WifiConfiguration config) { 975 if (!config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP)) return; 976 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE)) return; 977 978 Log.d(TAG, "Add upgradable Enterprise configuration."); 979 SecurityParams wpa3EnterpriseParams = SecurityParams.createSecurityParamsBySecurityType( 980 WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE); 981 wpa3EnterpriseParams.setIsAddedByAutoUpgrade(true); 982 config.addSecurityParams(wpa3EnterpriseParams); 983 } 984 985 /** 986 * Add upgradable securit type to the given wifi configuration. 987 * 988 * @param config the wifi configuration to be checked. 989 */ addUpgradableSecurityTypeIfNecessary(WifiConfiguration config)990 public static boolean addUpgradableSecurityTypeIfNecessary(WifiConfiguration config) { 991 try { 992 addOpenUpgradableSecurityTypeIfNecessary(config); 993 addPskUpgradableSecurityTypeIfNecessary(config); 994 addEapUpgradableSecurityTypeIfNecessary(config); 995 } catch (IllegalArgumentException e) { 996 Log.e(TAG, "Failed to add upgradable security type"); 997 return false; 998 } 999 return true; 1000 } 1001 1002 /** 1003 * For a upgradable type which is added by the auto-upgrade mechenism, it is only 1004 * matched when corresponding auto-upgrade features are enabled. 1005 */ shouldOmitAutoUpgradeParams(SecurityParams params)1006 private static boolean shouldOmitAutoUpgradeParams(SecurityParams params) { 1007 if (!params.isAddedByAutoUpgrade()) return false; 1008 1009 WifiGlobals wifiGlobals = WifiInjector.getInstance().getWifiGlobals(); 1010 1011 if (params.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE)) { 1012 return !wifiGlobals.isWpa3SaeUpgradeEnabled(); 1013 } 1014 if (params.isSecurityType(WifiConfiguration.SECURITY_TYPE_OWE)) { 1015 return !wifiGlobals.isOweUpgradeEnabled(); 1016 } 1017 return false; 1018 } 1019 isSecurityParamsSupported(SecurityParams params)1020 private static boolean isSecurityParamsSupported(SecurityParams params) { 1021 final long wifiFeatures = WifiInjector.getInstance() 1022 .getActiveModeWarden().getPrimaryClientModeManager() 1023 .getSupportedFeatures(); 1024 switch (params.getSecurityType()) { 1025 case WifiConfiguration.SECURITY_TYPE_SAE: 1026 return 0 != (wifiFeatures & WifiManager.WIFI_FEATURE_WPA3_SAE); 1027 case WifiConfiguration.SECURITY_TYPE_OWE: 1028 return 0 != (wifiFeatures & WifiManager.WIFI_FEATURE_OWE); 1029 } 1030 return true; 1031 } 1032 1033 /** 1034 * Check the security params is valid or not. 1035 * @param params the requesting security params. 1036 * @return true if it's valid; otherwise false. 1037 */ isSecurityParamsValid(SecurityParams params)1038 public static boolean isSecurityParamsValid(SecurityParams params) { 1039 if (!params.isEnabled()) return false; 1040 if (!isSecurityParamsSupported(params)) return false; 1041 return true; 1042 } 1043 1044 /** 1045 * General WifiConfiguration list sorting algorithm: 1046 * 1, Place the fully enabled networks first. 1047 * 2. Next place all the temporarily disabled networks. 1048 * 3. Place the permanently disabled networks last (Permanently disabled networks are removed 1049 * before WifiConfigManager uses this comparator today!). 1050 * 1051 * Among the networks with the same status, sort them in the order determined by the return of 1052 * {@link #compareNetworksWithSameStatus(WifiConfiguration, WifiConfiguration)} method 1053 * implementation. 1054 */ 1055 public abstract static class WifiConfigurationComparator implements 1056 Comparator<WifiConfiguration> { 1057 private static final int ENABLED_NETWORK_SCORE = 3; 1058 private static final int TEMPORARY_DISABLED_NETWORK_SCORE = 2; 1059 private static final int PERMANENTLY_DISABLED_NETWORK_SCORE = 1; 1060 1061 @Override compare(WifiConfiguration a, WifiConfiguration b)1062 public int compare(WifiConfiguration a, WifiConfiguration b) { 1063 int configAScore = getNetworkStatusScore(a); 1064 int configBScore = getNetworkStatusScore(b); 1065 if (configAScore == configBScore) { 1066 return compareNetworksWithSameStatus(a, b); 1067 } else { 1068 return Integer.compare(configBScore, configAScore); 1069 } 1070 } 1071 1072 // This needs to be implemented by the connected/disconnected PNO list comparator. compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b)1073 abstract int compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b); 1074 1075 /** 1076 * Returns an integer representing a score for each configuration. The scores are assigned 1077 * based on the status of the configuration. The scores are assigned according to the order: 1078 * Fully enabled network > Temporarily disabled network > Permanently disabled network. 1079 */ getNetworkStatusScore(WifiConfiguration config)1080 private int getNetworkStatusScore(WifiConfiguration config) { 1081 if (config.getNetworkSelectionStatus().isNetworkEnabled()) { 1082 return ENABLED_NETWORK_SCORE; 1083 } else if (config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) { 1084 return TEMPORARY_DISABLED_NETWORK_SCORE; 1085 } else { 1086 return PERMANENTLY_DISABLED_NETWORK_SCORE; 1087 } 1088 } 1089 } 1090 1091 /** 1092 * Convert multi-type configurations to a list of configurations with a single security type, 1093 * where a configuration with multiple security configurations will be converted to multiple 1094 * Wi-Fi configurations with a single security type. 1095 * For R or older releases, Settings/WifiTrackerLib does not handle multiple configurations 1096 * with the same SSID and will result in duplicate saved networks. As a result, just return 1097 * the merged configs directly. 1098 * 1099 * @param configs the list of multi-type configurations. 1100 * @return a list of Wi-Fi configurations with a single security type, 1101 * that may contain multiple configurations with the same network ID. 1102 */ convertMultiTypeConfigsToLegacyConfigs( List<WifiConfiguration> configs)1103 public static List<WifiConfiguration> convertMultiTypeConfigsToLegacyConfigs( 1104 List<WifiConfiguration> configs) { 1105 if (!SdkLevel.isAtLeastS()) { 1106 return configs; 1107 } 1108 List<WifiConfiguration> legacyConfigs = new ArrayList<>(); 1109 for (WifiConfiguration config : configs) { 1110 for (SecurityParams params: config.getSecurityParamsList()) { 1111 if (!params.isEnabled()) continue; 1112 if (shouldOmitAutoUpgradeParams(params)) continue; 1113 WifiConfiguration legacyConfig = new WifiConfiguration(config); 1114 legacyConfig.setSecurityParams(params); 1115 legacyConfigs.add(legacyConfig); 1116 } 1117 } 1118 return legacyConfigs; 1119 } 1120 validateEnterpriseConfig(WifiConfiguration config, boolean isAdd)1121 private static boolean validateEnterpriseConfig(WifiConfiguration config, boolean isAdd) { 1122 if ((config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP) 1123 || config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE)) 1124 && !config.isEnterprise()) { 1125 return false; 1126 } 1127 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT) 1128 && (!config.isEnterprise() 1129 || config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.TLS)) { 1130 return false; 1131 } 1132 if (config.isEnterprise()) { 1133 if (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP 1134 || config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS) { 1135 1136 int phase2Method = config.enterpriseConfig.getPhase2Method(); 1137 if (phase2Method == WifiEnterpriseConfig.Phase2.MSCHAP 1138 || phase2Method == WifiEnterpriseConfig.Phase2.MSCHAPV2 1139 || phase2Method == WifiEnterpriseConfig.Phase2.PAP 1140 || phase2Method == WifiEnterpriseConfig.Phase2.GTC) { 1141 // Check the password on add only. When updating, the password may not be 1142 // available and it appears as "(Unchanged)" in Settings 1143 if ((isAdd && TextUtils.isEmpty(config.enterpriseConfig.getPassword())) 1144 || TextUtils.isEmpty(config.enterpriseConfig.getIdentity())) { 1145 Log.e(TAG, "Enterprise network without an identity or a password set"); 1146 return false; 1147 } 1148 } 1149 } 1150 } 1151 return true; 1152 } 1153 } 1154