blob: a9f772011b295a2c7cfdae91cdec0e912711151b [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wifi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.wifi.ScanResult;
import android.net.wifi.SecurityParams;
import android.net.wifi.WifiConfiguration;
import com.android.server.wifi.util.ScanResultUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Class to store the info needed to match a scan result to the provided network configuration.
*/
public class ScanResultMatchInfo {
/**
* SSID of the network.
*/
public String networkSsid;
/**
* Security params list.
*/
public List<SecurityParams> securityParamsList = new ArrayList<>();
/**
* True if created from a scan result
*/
private boolean mFromScanResult = false;
/**
* Get the ScanResultMatchInfo for the given WifiConfiguration
*/
public static ScanResultMatchInfo fromWifiConfiguration(WifiConfiguration config) {
ScanResultMatchInfo info = new ScanResultMatchInfo();
info.networkSsid = config.SSID;
info.securityParamsList = config.getSecurityParamsList();
return info;
}
/**
* Get the ScanResultMatchInfo for the given ScanResult
*/
public static ScanResultMatchInfo fromScanResult(ScanResult scanResult) {
ScanResultMatchInfo info = new ScanResultMatchInfo();
// Scan result ssid's are not quoted, hence add quotes.
// TODO: This matching algo works only if the scan result contains a string SSID.
// However, according to our public documentation ths {@link WifiConfiguration#SSID} can
// either have a hex string or quoted ASCII string SSID.
info.networkSsid = ScanResultUtil.createQuotedSSID(scanResult.SSID);
info.securityParamsList =
ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
info.mFromScanResult = true;
return info;
}
/**
* Check if an auto-upgraded security parameters configuration is allowed by the overlay
* configurations for WPA3-Personal (SAE) and Enhanced Open (OWE).
*
* @param securityParams Security parameters object
* @return true if allowed, false if not allowed
*/
private static boolean isAutoUpgradeSecurityParamsAllowed(SecurityParams securityParams) {
WifiGlobals wifiGlobals = WifiInjector.getInstance().getWifiGlobals();
// In mixed security network environments, we need to filter out APs with the stronger
// security type when the current network supports the weaker security type, and the
// stronger security type was added by auto-upgrade, and
// auto-upgrade feature is disabled.
if (securityParams.getSecurityType() == WifiConfiguration.SECURITY_TYPE_SAE
&& securityParams.isAddedByAutoUpgrade()
&& !wifiGlobals.isWpa3SaeUpgradeEnabled()) {
return false;
}
if (securityParams.getSecurityType() == WifiConfiguration.SECURITY_TYPE_OWE
&& securityParams.isAddedByAutoUpgrade()
&& !wifiGlobals.isOweUpgradeEnabled()) {
return false;
}
return true;
}
/**
* The matching algorithm is that the type with a bigger index in the allowed
* params list has the higher priority. We try to match each type from the end of
* the allowed params list against the params in the scan result params list.
*
* There are three cases which will skip the match:
* 1. the security type is different.
* 2. the params is disabled, ex. disabled by Transition Disable Indication.
* 3. The params is added by the auto-upgrade mechanism, but the corresponding
* feature is not enabled.
*/
private static @Nullable SecurityParams findBestMatchingSecurityParams(
List<SecurityParams> allowedParamsList,
List<SecurityParams> scanResultParamsList) {
if (null == allowedParamsList) return null;
if (null == scanResultParamsList) return null;
for (int i = allowedParamsList.size() - 1; i >= 0; i--) {
SecurityParams allowedParams = allowedParamsList.get(i);
if (!WifiConfigurationUtil.isSecurityParamsValid(allowedParams)
|| !isAutoUpgradeSecurityParamsAllowed(allowedParams)) {
continue;
}
for (SecurityParams scanResultParams: scanResultParamsList) {
if (!allowedParams.isSecurityType(scanResultParams.getSecurityType())) {
continue;
}
return allowedParams;
}
}
return null;
}
/**
* Get the best-matching security type between ScanResult and WifiConifiguration.
*/
public static @Nullable SecurityParams getBestMatchingSecurityParams(
WifiConfiguration config,
ScanResult scanResult) {
if (null == config || null == scanResult) return null;
return findBestMatchingSecurityParams(
config.getSecurityParamsList(),
ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult));
}
/**
* Get the best-matching security type between ScanResult and WifiConifiguration.
*/
public static @Nullable SecurityParams getBestMatchingSecurityParams(
WifiConfiguration config,
List<SecurityParams> scanResultParamsList) {
if (null == config || null == scanResultParamsList) return null;
return findBestMatchingSecurityParams(
config.getSecurityParamsList(),
scanResultParamsList);
}
public @Nullable SecurityParams getDefaultSecurityParams() {
return securityParamsList.isEmpty() ? null : securityParamsList.get(0);
}
public @Nullable SecurityParams getFirstAvailableSecurityParams() {
return securityParamsList.stream()
.filter(WifiConfigurationUtil::isSecurityParamsValid)
.findFirst()
.orElse(null);
}
/**
* Checks for equality of network type.
*/
public boolean networkTypeEquals(@NonNull ScanResultMatchInfo other) {
if (null == securityParamsList || null == other.securityParamsList) return false;
// If both are from the same sources, do normal comparison.
if (mFromScanResult == other.mFromScanResult) {
return securityParamsList.equals(other.securityParamsList);
}
final List<SecurityParams> allowedParamsList = mFromScanResult
? other.securityParamsList : securityParamsList;
final List<SecurityParams> scanResultParamsList = mFromScanResult
? securityParamsList : other.securityParamsList;
return null != findBestMatchingSecurityParams(
allowedParamsList,
scanResultParamsList);
}
@Override
public boolean equals(Object otherObj) {
if (this == otherObj) {
return true;
} else if (!(otherObj instanceof ScanResultMatchInfo)) {
return false;
}
ScanResultMatchInfo other = (ScanResultMatchInfo) otherObj;
if (mFromScanResult == other.mFromScanResult) {
return Objects.equals(networkSsid, other.networkSsid)
&& securityParamsList.equals(other.securityParamsList);
}
return null != matchForNetworkSelection(other);
}
/**
* Match two ScanResultMatchInfo objects while considering configuration in overlays
*
* @param other Other object to compare against
* @return return best matching security params, null if no matching one.
*/
public SecurityParams matchForNetworkSelection(ScanResultMatchInfo other) {
if (!Objects.equals(networkSsid, other.networkSsid)) return null;
if (null == securityParamsList) return null;
if (null == other.securityParamsList) return null;
final List<SecurityParams> allowedParamsList = mFromScanResult
? other.securityParamsList : securityParamsList;
final List<SecurityParams> scanResultParamsList = mFromScanResult
? securityParamsList : other.securityParamsList;
return findBestMatchingSecurityParams(
allowedParamsList,
scanResultParamsList);
}
/** Check whether this matchinfo contains the type or not. */
public boolean isSecurityType(@WifiConfiguration.SecurityType int securityType) {
return securityParamsList.stream().anyMatch(p -> p.isSecurityType(securityType));
}
@Override
public int hashCode() {
return Objects.hash(networkSsid);
}
@Override
public String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append("ScanResultMatchInfo: SSID: ").append(networkSsid);
sbuf.append(", from scan result: ").append(mFromScanResult);
sbuf.append(", SecurityParams List:");
securityParamsList.stream()
.forEach(params -> sbuf.append(params.toString()));
return sbuf.toString();
}
}