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.annotation.Nullable;
20 import android.net.wifi.ScanResult;
21 import android.net.wifi.SecurityParams;
22 import android.net.wifi.WifiConfiguration;
23 import android.net.wifi.WifiSsid;
24 import android.net.wifi.util.ScanResultUtil;
25 
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.Objects;
29 
30 /**
31  * Class to store the info needed to match a scan result to the provided network configuration.
32  */
33 public class ScanResultMatchInfo {
34     /**
35      * SSID of the network.
36      */
37     public String networkSsid;
38     /**
39      * Security params list.
40      */
41     public List<SecurityParams> securityParamsList = new ArrayList<>();
42 
43     /**
44      * True if created from a scan result
45      */
46     private boolean mFromScanResult = false;
47 
48     /**
49      * Get the ScanResultMatchInfo for the given WifiConfiguration
50      */
fromWifiConfiguration(WifiConfiguration config)51     public static ScanResultMatchInfo fromWifiConfiguration(WifiConfiguration config) {
52         ScanResultMatchInfo info = new ScanResultMatchInfo();
53         info.networkSsid = config.SSID;
54         info.securityParamsList = config.getSecurityParamsList();
55         return info;
56     }
57 
58     /**
59      * Get the ScanResultMatchInfo for the given ScanResult
60      */
fromScanResult(ScanResult scanResult)61     public static ScanResultMatchInfo fromScanResult(ScanResult scanResult) {
62         ScanResultMatchInfo info = new ScanResultMatchInfo();
63         WifiSsid wifiSsid = scanResult.getWifiSsid();
64         if (wifiSsid != null) {
65             info.networkSsid = wifiSsid.toString();
66         } else {
67             info.networkSsid = "\"" + scanResult.SSID + "\"";
68         }
69         info.securityParamsList =
70                 ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
71         info.mFromScanResult = true;
72         return info;
73     }
74 
75     /**
76      * Check if an auto-upgraded security parameters configuration is allowed by the overlay
77      * configurations for WPA3-Personal (SAE) and Enhanced Open (OWE).
78      *
79      * @param securityParams Security parameters object
80      * @return true if allowed, false if not allowed
81      */
isAutoUpgradeSecurityParamsAllowed(SecurityParams securityParams)82     private static boolean isAutoUpgradeSecurityParamsAllowed(SecurityParams securityParams) {
83         WifiGlobals wifiGlobals = WifiInjector.getInstance().getWifiGlobals();
84         // In mixed security network environments, we need to filter out APs with the stronger
85         // security type when the current network supports the weaker security type, and the
86         // stronger security type was added by auto-upgrade, and
87         // auto-upgrade feature is disabled.
88         if (securityParams.getSecurityType() == WifiConfiguration.SECURITY_TYPE_SAE
89                 && securityParams.isAddedByAutoUpgrade()
90                 && !wifiGlobals.isWpa3SaeUpgradeEnabled()) {
91             return false;
92         }
93         if (securityParams.getSecurityType() == WifiConfiguration.SECURITY_TYPE_OWE
94                 && securityParams.isAddedByAutoUpgrade()
95                 && !wifiGlobals.isOweUpgradeEnabled()) {
96             return false;
97         }
98         return true;
99     }
100 
101     /**
102      * The matching algorithm is that the type with a bigger index in the allowed
103      * params list has the higher priority. We try to match each type from the end of
104      * the allowed params list against the params in the scan result params list.
105      *
106      * There are three cases which will skip the match:
107      * 1. the security type is different.
108      * 2. the params is disabled, ex. disabled by Transition Disable Indication.
109      * 3. The params is added by the auto-upgrade mechanism, but the corresponding
110      *    feature is not enabled.
111      */
findBestMatchingSecurityParams( List<SecurityParams> allowedParamsList, List<SecurityParams> scanResultParamsList)112     private static @Nullable SecurityParams findBestMatchingSecurityParams(
113             List<SecurityParams> allowedParamsList,
114             List<SecurityParams> scanResultParamsList) {
115         if (null == allowedParamsList) return null;
116         if (null == scanResultParamsList) return null;
117         for (int i = allowedParamsList.size() - 1; i >= 0; i--) {
118             SecurityParams allowedParams = allowedParamsList.get(i);
119             if (!WifiConfigurationUtil.isSecurityParamsValid(allowedParams)
120                     || !isAutoUpgradeSecurityParamsAllowed(allowedParams)) {
121                 continue;
122             }
123             for (SecurityParams scanResultParams: scanResultParamsList) {
124                 if (!allowedParams.isSecurityType(scanResultParams.getSecurityType())) {
125                     continue;
126                 }
127                 return allowedParams;
128             }
129         }
130         return null;
131     }
132 
133     /**
134      * Get the best-matching security type between ScanResult and WifiConifiguration.
135      */
getBestMatchingSecurityParams( WifiConfiguration config, ScanResult scanResult)136     public static @Nullable SecurityParams getBestMatchingSecurityParams(
137             WifiConfiguration config,
138             ScanResult scanResult) {
139         if (null == config || null == scanResult) return null;
140 
141         return findBestMatchingSecurityParams(
142                 config.getSecurityParamsList(),
143                 ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult));
144     }
145 
146     /**
147      * Get the best-matching security type between ScanResult and WifiConifiguration.
148      */
getBestMatchingSecurityParams( WifiConfiguration config, List<SecurityParams> scanResultParamsList)149     public static @Nullable SecurityParams getBestMatchingSecurityParams(
150             WifiConfiguration config,
151             List<SecurityParams> scanResultParamsList) {
152         if (null == config || null == scanResultParamsList) return null;
153 
154         return findBestMatchingSecurityParams(
155                 config.getSecurityParamsList(),
156                 scanResultParamsList);
157     }
158 
getDefaultSecurityParams()159     public @Nullable SecurityParams getDefaultSecurityParams() {
160         return securityParamsList.isEmpty() ? null : securityParamsList.get(0);
161     }
162 
getFirstAvailableSecurityParams()163     public @Nullable SecurityParams getFirstAvailableSecurityParams() {
164         return securityParamsList.stream()
165                 .filter(WifiConfigurationUtil::isSecurityParamsValid)
166                 .findFirst()
167                 .orElse(null);
168     }
169 
170     /**
171      * Checks for equality of network type.
172      */
networkTypeEquals(@onNull ScanResultMatchInfo other)173     public boolean networkTypeEquals(@NonNull ScanResultMatchInfo other) {
174         if (null == securityParamsList || null == other.securityParamsList) return false;
175 
176         // If both are from the same sources, do normal comparison.
177         if (mFromScanResult == other.mFromScanResult) {
178             return securityParamsList.equals(other.securityParamsList);
179         }
180 
181         final List<SecurityParams> allowedParamsList = mFromScanResult
182                 ? other.securityParamsList : securityParamsList;
183         final List<SecurityParams> scanResultParamsList = mFromScanResult
184                 ? securityParamsList : other.securityParamsList;
185 
186         return null != findBestMatchingSecurityParams(
187                 allowedParamsList,
188                 scanResultParamsList);
189     }
190 
191     @Override
equals(Object otherObj)192     public boolean equals(Object otherObj) {
193         if (this == otherObj) {
194             return true;
195         } else if (!(otherObj instanceof ScanResultMatchInfo)) {
196             return false;
197         }
198         ScanResultMatchInfo other = (ScanResultMatchInfo) otherObj;
199         if (mFromScanResult == other.mFromScanResult) {
200             return Objects.equals(networkSsid, other.networkSsid)
201                     && securityParamsList.equals(other.securityParamsList);
202         }
203         return null != matchForNetworkSelection(other);
204     }
205 
206     /**
207      * Match two ScanResultMatchInfo objects while considering configuration in overlays
208      *
209      * @param other Other object to compare against
210      * @return return best matching security params, null if no matching one.
211      */
matchForNetworkSelection(ScanResultMatchInfo other)212     public SecurityParams matchForNetworkSelection(ScanResultMatchInfo other) {
213         if (!Objects.equals(networkSsid, other.networkSsid)) return null;
214         if (null == securityParamsList) return null;
215         if (null == other.securityParamsList) return null;
216 
217         final List<SecurityParams> allowedParamsList = mFromScanResult
218                 ? other.securityParamsList : securityParamsList;
219         final List<SecurityParams> scanResultParamsList = mFromScanResult
220                 ? securityParamsList : other.securityParamsList;
221 
222         return findBestMatchingSecurityParams(
223                 allowedParamsList,
224                 scanResultParamsList);
225     }
226 
227     /** Check whether this matchinfo contains the type or not. */
isSecurityType(@ifiConfiguration.SecurityType int securityType)228     public boolean isSecurityType(@WifiConfiguration.SecurityType int securityType) {
229         return securityParamsList.stream().anyMatch(p -> p.isSecurityType(securityType));
230     }
231 
232     @Override
hashCode()233     public int hashCode() {
234         return Objects.hash(networkSsid);
235     }
236 
237     @Override
toString()238     public String toString() {
239         StringBuffer sbuf = new StringBuffer();
240         sbuf.append("ScanResultMatchInfo: SSID: ").append(networkSsid);
241         sbuf.append(", from scan result: ").append(mFromScanResult);
242         sbuf.append(", SecurityParams List:");
243         securityParamsList.stream()
244                 .forEach(params -> sbuf.append(params.toString()));
245         return sbuf.toString();
246     }
247 }
248