1 /*
2  * Copyright (C) 2017 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 package com.android.server.wifi;
17 
18 import android.annotation.NonNull;
19 import android.net.wifi.ScanResult;
20 import android.net.wifi.WifiConfiguration;
21 
22 import com.android.server.wifi.util.ScanResultUtil;
23 
24 import java.util.Objects;
25 
26 /**
27  * Class to store the info needed to match a scan result to the provided network configuration.
28  */
29 public class ScanResultMatchInfo {
30     /**
31      * SSID of the network.
32      */
33     public String networkSsid;
34     /**
35      * Security Type of the network.
36      */
37     public @WifiConfiguration.SecurityType int networkType;
38     /**
39      * Special flag for PSK-SAE in transition mode
40      */
41     public boolean pskSaeInTransitionMode;
42     /**
43      * Special flag for OWE in transition mode
44      */
45     public boolean oweInTransitionMode;
46 
47     /**
48      * True if created from a scan result
49      */
50     private boolean mFromScanResult = false;
51     /**
52      * Fetch network type from network configuration.
53      */
getNetworkType(WifiConfiguration config)54     private static @WifiConfiguration.SecurityType int getNetworkType(WifiConfiguration config) {
55         if (WifiConfigurationUtil.isConfigForSaeNetwork(config)) {
56             return WifiConfiguration.SECURITY_TYPE_SAE;
57         } else if (WifiConfigurationUtil.isConfigForPskNetwork(config)) {
58             return WifiConfiguration.SECURITY_TYPE_PSK;
59         } else if (WifiConfigurationUtil.isConfigForWapiPskNetwork(config)) {
60             return WifiConfiguration.SECURITY_TYPE_WAPI_PSK;
61         } else if (WifiConfigurationUtil.isConfigForWapiCertNetwork(config)) {
62             return WifiConfiguration.SECURITY_TYPE_WAPI_CERT;
63         } else if (WifiConfigurationUtil.isConfigForEapNetwork(config)) {
64             return WifiConfiguration.SECURITY_TYPE_EAP;
65         } else if (WifiConfigurationUtil.isConfigForEapSuiteBNetwork(config)) {
66             return WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B;
67         } else if (WifiConfigurationUtil.isConfigForWepNetwork(config)) {
68             return WifiConfiguration.SECURITY_TYPE_WEP;
69         } else if (WifiConfigurationUtil.isConfigForOweNetwork(config)) {
70             return WifiConfiguration.SECURITY_TYPE_OWE;
71         } else if (WifiConfigurationUtil.isConfigForOpenNetwork(config)) {
72             return WifiConfiguration.SECURITY_TYPE_OPEN;
73         }
74         throw new IllegalArgumentException("Invalid WifiConfiguration: " + config);
75     }
76 
77     /**
78      * Get the ScanResultMatchInfo for the given WifiConfiguration
79      */
fromWifiConfiguration(WifiConfiguration config)80     public static ScanResultMatchInfo fromWifiConfiguration(WifiConfiguration config) {
81         ScanResultMatchInfo info = new ScanResultMatchInfo();
82         info.networkSsid = config.SSID;
83         info.networkType = getNetworkType(config);
84         return info;
85     }
86 
87     /**
88      * Fetch network type from scan result.
89      */
getNetworkType(ScanResult scanResult)90     private static @WifiConfiguration.SecurityType int getNetworkType(ScanResult scanResult) {
91         if (ScanResultUtil.isScanResultForSaeNetwork(scanResult)) {
92             return WifiConfiguration.SECURITY_TYPE_SAE;
93         } else if (ScanResultUtil.isScanResultForWapiPskNetwork(scanResult)) {
94             return WifiConfiguration.SECURITY_TYPE_WAPI_PSK;
95         } else if (ScanResultUtil.isScanResultForWapiCertNetwork(scanResult)) {
96             return WifiConfiguration.SECURITY_TYPE_WAPI_CERT;
97         } else if (ScanResultUtil.isScanResultForPskNetwork(scanResult)) {
98             return WifiConfiguration.SECURITY_TYPE_PSK;
99         } else if (ScanResultUtil.isScanResultForEapSuiteBNetwork(scanResult)) {
100             return WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B;
101         } else if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) {
102             return WifiConfiguration.SECURITY_TYPE_EAP;
103         } else if (ScanResultUtil.isScanResultForWepNetwork(scanResult)) {
104             return WifiConfiguration.SECURITY_TYPE_WEP;
105         } else if (ScanResultUtil.isScanResultForOweNetwork(scanResult)) {
106             return WifiConfiguration.SECURITY_TYPE_OWE;
107         } else if (ScanResultUtil.isScanResultForOpenNetwork(scanResult)) {
108             return WifiConfiguration.SECURITY_TYPE_OPEN;
109         } else {
110             throw new IllegalArgumentException("Invalid ScanResult: " + scanResult);
111         }
112     }
113 
114     /**
115      * Get the ScanResultMatchInfo for the given ScanResult
116      */
fromScanResult(ScanResult scanResult)117     public static ScanResultMatchInfo fromScanResult(ScanResult scanResult) {
118         ScanResultMatchInfo info = new ScanResultMatchInfo();
119         // Scan result ssid's are not quoted, hence add quotes.
120         // TODO: This matching algo works only if the scan result contains a string SSID.
121         // However, according to our public documentation ths {@link WifiConfiguration#SSID} can
122         // either have a hex string or quoted ASCII string SSID.
123         info.networkSsid = ScanResultUtil.createQuotedSSID(scanResult.SSID);
124         info.networkType = getNetworkType(scanResult);
125         info.oweInTransitionMode = false;
126         info.pskSaeInTransitionMode = false;
127         info.mFromScanResult = true;
128         if (info.networkType == WifiConfiguration.SECURITY_TYPE_SAE) {
129             // Note that scan result util will always choose the highest security protocol.
130             info.pskSaeInTransitionMode =
131                     ScanResultUtil.isScanResultForPskSaeTransitionNetwork(scanResult);
132         } else  if (info.networkType == WifiConfiguration.SECURITY_TYPE_OWE) {
133             // Note that scan result util will always choose OWE.
134             info.oweInTransitionMode =
135                     ScanResultUtil.isScanResultForOweTransitionNetwork(scanResult);
136         }
137         return info;
138     }
139 
140     /**
141      * Checks for equality of network type.
142      */
networkTypeEquals(@onNull ScanResultMatchInfo other, boolean saeAutoUpgradeEnabled)143     public boolean networkTypeEquals(@NonNull ScanResultMatchInfo other,
144             boolean saeAutoUpgradeEnabled) {
145         boolean networkTypeEquals;
146         // Detect <SSID, PSK+SAE> scan result and say it is equal to <SSID, PSK> configuration
147         if (other.pskSaeInTransitionMode && networkType == WifiConfiguration.SECURITY_TYPE_PSK
148                 || (pskSaeInTransitionMode
149                 && other.networkType == WifiConfiguration.SECURITY_TYPE_PSK)) {
150             networkTypeEquals = true;
151         } else if ((networkType == WifiConfiguration.SECURITY_TYPE_OPEN
152                 && other.oweInTransitionMode) || (oweInTransitionMode
153                 && other.networkType == WifiConfiguration.SECURITY_TYPE_OPEN)) {
154             // Special case we treat Enhanced Open and Open as equals. This is done to support the
155             // case where a saved network is Open but we found an OWE in transition network.
156             networkTypeEquals = true;
157         } else if ((saeAutoUpgradeEnabled)
158                 && ((mFromScanResult && networkType == WifiConfiguration.SECURITY_TYPE_SAE
159                 && other.networkType == WifiConfiguration.SECURITY_TYPE_PSK)
160                 || (other.mFromScanResult
161                 && other.networkType == WifiConfiguration.SECURITY_TYPE_SAE
162                 && networkType == WifiConfiguration.SECURITY_TYPE_PSK))) {
163             // Allow upgrading WPA2 PSK connections to WPA3 SAE AP
164             networkTypeEquals = true;
165         } else {
166             networkTypeEquals = networkType == other.networkType;
167         }
168         return networkTypeEquals;
169     }
170 
171     @Override
equals(Object otherObj)172     public boolean equals(Object otherObj) {
173         return matchForNetworkSelection(otherObj, false);
174     }
175 
176     /**
177      * Match two ScanResultMatchInfo objects while considering configuration in overlays
178      *
179      * @param otherObj Other object to compare against
180      * @param saeAutoUpgradeEnabled A boolean that indicates if WPA3 auto upgrade feature is enabled
181      * @return true if objects are equal for network selection purposes, false otherwise
182      */
matchForNetworkSelection(Object otherObj, boolean saeAutoUpgradeEnabled)183     public boolean matchForNetworkSelection(Object otherObj, boolean saeAutoUpgradeEnabled) {
184         if (this == otherObj) {
185             return true;
186         } else if (!(otherObj instanceof ScanResultMatchInfo)) {
187             return false;
188         }
189         ScanResultMatchInfo other = (ScanResultMatchInfo) otherObj;
190         if (!Objects.equals(networkSsid, other.networkSsid)) {
191             return false;
192         }
193         return networkTypeEquals(other, saeAutoUpgradeEnabled);
194     }
195 
196     @Override
hashCode()197     public int hashCode() {
198         return Objects.hash(networkSsid);
199     }
200 
201     @Override
toString()202     public String toString() {
203         return "ScanResultMatchInfo: SSID: " + networkSsid + ", type: " + networkType
204                 + ", WPA3 in transition mode: " + pskSaeInTransitionMode
205                 + ", OWE in transition mode: " + oweInTransitionMode + ", from scan result: "
206                 + mFromScanResult;
207     }
208 }
209