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