• 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.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