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