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