blob: f134ee5d4938414780cdc787b71ab3aa84c4c981 [file] [log] [blame]
/*
* Copyright (C) 2016 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 static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_PSK;
import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_SAE;
import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_WAPI_PSK;
import static com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import android.app.test.MockAnswerUtil.AnswerWithArguments;
import android.net.NetworkKey;
import android.net.RssiCurve;
import android.net.ScoredNetwork;
import android.net.WifiKey;
import android.net.wifi.ScanResult;
import android.net.wifi.SecurityParams;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiSsid;
import android.text.TextUtils;
import com.android.server.wifi.hotspot2.NetworkDetail;
import com.android.server.wifi.util.InformationElementUtil;
import com.android.server.wifi.util.NativeUtil;
import com.android.server.wifi.util.ScanResultUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Helper for WifiNetworkSelector unit tests.
*/
public class WifiNetworkSelectorTestUtil {
private static final String TAG = "WifiNetworkSelectorTestUtil";
/**
* A class that holds a list of scanDetail and their associated WifiConfiguration.
*/
public static class ScanDetailsAndWifiConfigs {
List<ScanDetail> mScanDetails;
WifiConfiguration[] mWifiConfigs;
ScanDetailsAndWifiConfigs(List<ScanDetail> scanDetails, WifiConfiguration[] configs) {
mScanDetails = scanDetails;
mWifiConfigs = configs;
}
List<ScanDetail> getScanDetails() {
return mScanDetails;
}
WifiConfiguration[] getWifiConfigs() {
return mWifiConfigs;
}
}
/**
* Build a list of ScanDetail based on the caller supplied network SSID, BSSID,
* frequency, capability and RSSI level information. Create the corresponding
* WifiConfiguration for these networks and set up the mocked WifiConfigManager.
*
* @param ssids an array of SSIDs
* @param bssids an array of BSSIDs
* @param freqs an array of the network's frequency
* @param caps an array of the network's capability
* @param levels an array of the network's RSSI levels
* @param securities an array of the network's security setting
* @param wifiConfigManager the mocked WifiConfigManager
* @return the constructed ScanDetail list and WifiConfiguration array
*/
public static ScanDetailsAndWifiConfigs setupScanDetailsAndConfigStore(String[] ssids,
String[] bssids, int[] freqs, String[] caps, int[] levels, int[] securities,
WifiConfigManager wifiConfigManager, Clock clock) {
List<ScanDetail> scanDetails = buildScanDetails(ssids, bssids, freqs, caps, levels, clock);
WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, securities);
addWifiConfigAndLinkScanResult(wifiConfigManager, savedConfigs, scanDetails);
return new ScanDetailsAndWifiConfigs(scanDetails, savedConfigs);
}
public static ScanDetailsAndWifiConfigs setupScanDetailsAndConfigStore(String[] ssids,
String[] bssids, int[] freqs, String[] caps, int[] levels,
int[] securities, WifiConfigManager wifiConfigManager, Clock clock,
byte[][] iesByteStream) {
if (iesByteStream == null) {
throw new IllegalArgumentException("Null ies");
}
List<ScanDetail> scanDetails = buildScanDetailsWithNetworkDetails(ssids, bssids, freqs,
caps, levels, iesByteStream, clock);
WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, securities);
addWifiConfigAndLinkScanResult(wifiConfigManager, savedConfigs, scanDetails);
return new ScanDetailsAndWifiConfigs(scanDetails, savedConfigs);
}
/**
* Build a list of ScanDetail based on the caller supplied network SSID, BSSID,
* frequency and RSSI level information. Create the EAP-SIM authticated
* WifiConfiguration for these networks and set up the mocked WifiConfigManager.
*
* @param ssids an array of SSIDs
* @param bssids an array of BSSIDs
* @param freqs an array of the network's frequency
* @param levels an array of the network's RSSI levels
* @param wifiConfigManager the mocked WifiConfigManager
* @return the constructed ScanDetail list and WifiConfiguration array
*/
public static ScanDetailsAndWifiConfigs setupScanDetailsAndConfigForEapSimNetwork(
String[] ssids,
String[] bssids, int[] freqs, int[] levels,
WifiConfigManager wifiConfigManager, Clock clock) {
assertNotNull(ssids);
String[] caps = new String[ssids.length];
for (int i = 0; i < ssids.length; i++) {
caps[i] = "[EAP/SHA1][ESS]";
}
List<ScanDetail> scanDetails = buildScanDetails(ssids, bssids, freqs, caps, levels, clock);
WifiConfiguration[] savedConfigs = new WifiConfiguration[ssids.length];
Set<String> ssidSet = new HashSet<>();
for (int i = 0; i < ssids.length; i++) {
// do not allow duplicated ssid
assertFalse(ssidSet.contains(ssids[i]));
ssidSet.add(ssids[i]);
savedConfigs[i] = WifiConfigurationTestUtil.createEapNetwork(
WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE);
savedConfigs[i].SSID = ssids[i];
savedConfigs[i].networkId = i;
}
addWifiConfigAndLinkScanResult(wifiConfigManager, savedConfigs, scanDetails);
return new ScanDetailsAndWifiConfigs(scanDetails, savedConfigs);
}
private static void addWifiConfigAndLinkScanResult(WifiConfigManager wifiConfigManager,
WifiConfiguration[] configs, List<ScanDetail> scanDetails) {
checkConsistencyOfScanDetailsAndWifiConfigs(scanDetails, configs);
prepareConfigStore(wifiConfigManager, configs);
scanResultLinkConfiguration(wifiConfigManager, configs, scanDetails);
}
private static void checkConsistencyOfScanDetailsAndWifiConfigs(
List<ScanDetail> scanDetails,
WifiConfiguration[] savedConfigs) {
assertEquals(scanDetails.size(), savedConfigs.length);
for (int i = 0; i < savedConfigs.length; i++) {
ScanResult scanResult = scanDetails.get(i).getScanResult();
WifiConfiguration config = savedConfigs[i];
// Can check this only for configs with a single security type
if (config.getSecurityParamsList().size() < 2) {
assertEquals("Problem in entry " + i,
ScanResultMatchInfo.fromScanResult(scanResult),
ScanResultMatchInfo.fromWifiConfiguration(config));
}
}
}
/**
* Verify whether the WifiConfiguration chosen by WifiNetworkSelector matches
* with the chosen scan result.
*
* @param chosenScanResult the chosen scan result
* @param chosenCandidate the chosen configuration
*/
public static void verifySelectedScanResult(WifiConfigManager wifiConfigManager,
ScanResult chosenScanResult, WifiConfiguration chosenCandidate) {
verify(wifiConfigManager, atLeastOnce()).setNetworkCandidateScanResult(
eq(chosenCandidate.networkId), eq(chosenScanResult), anyInt(),
eq(chosenCandidate.getSecurityParamsList().get(0)));
}
/**
* Build a list of scanDetails based on the caller supplied network SSID, BSSID,
* frequency, capability and RSSI level information.
*
* @param ssids an array of SSIDs
* @param bssids an array of BSSIDs
* @param freqs an array of the network's frequency
* @param caps an array of the network's capability
* @param levels an array of the network's RSSI levels
* @return the constructed list of ScanDetail
*/
public static List<ScanDetail> buildScanDetails(String[] ssids, String[] bssids, int[] freqs,
String[] caps, int[] levels, Clock clock) {
List<ScanDetail> scanDetailList = new ArrayList<ScanDetail>();
long timeStamp = clock.getElapsedSinceBootMillis();
for (int index = 0; index < ssids.length; index++) {
byte[] ssid = NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(ssids[index]));
ScanDetail scanDetail = new ScanDetail(WifiSsid.createFromByteArray(ssid),
bssids[index], caps[index], levels[index], freqs[index], timeStamp, 0);
scanDetailList.add(scanDetail);
}
return scanDetailList;
}
/**
* Build a list of scanDetails along with network details based
* on the caller supplied network SSID, BSSID, frequency,
* capability, byte stream of IEs and RSSI level information.
*
* @param ssids an array of SSIDs
* @param bssids an array of BSSIDs
* @param freqs an array of the network's frequency
* @param caps an array of the network's capability
* @param levels an array of the network's RSSI levels
* @return the constructed list of ScanDetail
*/
public static List<ScanDetail> buildScanDetailsWithNetworkDetails(String[] ssids,
String[] bssids, int[] freqs,
String[] caps, int[] levels, byte[][] iesByteStream, Clock clock) {
List<ScanDetail> scanDetailList = new ArrayList<ScanDetail>();
long timeStamp = clock.getElapsedSinceBootMillis();
for (int index = 0; index < ssids.length; index++) {
byte[] ssid = NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(ssids[index]));
ScanResult.InformationElement[] ies =
InformationElementUtil.parseInformationElements(iesByteStream[index]);
NetworkDetail nd = new NetworkDetail(bssids[index], ies, new ArrayList<String>(),
freqs[index]);
ScanDetail scanDetail = new ScanDetail(nd, WifiSsid.createFromByteArray(ssid),
bssids[index], caps[index], levels[index], freqs[index], timeStamp,
ies, new ArrayList<String>(),
ScanResults.generateIERawDatafromScanResultIE(ies));
scanDetailList.add(scanDetail);
}
return scanDetailList;
}
/**
* Generate an array of {@link android.net.wifi.WifiConfiguration} based on the caller
* supplied network SSID and security information.
*
* @param ssids an array of SSIDs
* @param securities an array of the network's security setting
* @return the constructed array of {@link android.net.wifi.WifiConfiguration}
*/
public static WifiConfiguration[] generateWifiConfigurations(String[] ssids,
int[] securities) {
if (ssids == null || securities == null || ssids.length != securities.length) {
throw new IllegalArgumentException();
}
Map<String, Integer> netIdMap = new HashMap<>();
int netId = 0;
WifiConfiguration[] configs = new WifiConfiguration[ssids.length];
for (int index = 0; index < ssids.length; index++) {
String configKey = ssids[index] + securities[index];
Integer id = netIdMap.get(configKey);
if (id == null) {
id = netId;
netIdMap.put(configKey, id);
netId++;
}
configs[index] = generateWifiConfig(id, 0, ssids[index], false, true, null,
null, securities[index]);
if ((securities[index] & SECURITY_PSK) != 0 || (securities[index] & SECURITY_SAE) != 0
|| (securities[index] & SECURITY_WAPI_PSK) != 0) {
configs[index].preSharedKey = "\"PA55W0RD\""; // needed to validate with PSK
}
if (!WifiConfigurationUtil.validate(configs[index], true)) {
throw new IllegalArgumentException("Invalid generated config: " + configs[index]);
}
}
return configs;
}
/**
* Add the Configurations to WifiConfigManager (WifiConfigureStore can take them out according
* to the networkd ID) and setup the WifiConfigManager mocks for these networks.
* This simulates the WifiConfigManager class behaviour.
*
* @param wifiConfigManager the mocked WifiConfigManager
* @param configs input configuration need to be added to WifiConfigureStore
*/
public static void prepareConfigStore(final WifiConfigManager wifiConfigManager,
final WifiConfiguration[] configs) {
when(wifiConfigManager.getSavedNetworkForScanDetail(any(ScanDetail.class)))
.then(new AnswerWithArguments() {
public WifiConfiguration answer(ScanDetail scanDetail) {
for (WifiConfiguration config : configs) {
if (TextUtils.equals(config.SSID,
ScanResultUtil.createQuotedSSID(scanDetail.getSSID()))) {
return config;
}
}
return null;
}
});
when(wifiConfigManager.getConfiguredNetwork(anyInt()))
.then(new AnswerWithArguments() {
public WifiConfiguration answer(int netId) {
for (WifiConfiguration config : configs) {
if (netId == config.networkId) {
return new WifiConfiguration(config);
}
}
return null;
}
});
when(wifiConfigManager.getConfiguredNetwork(anyString()))
.then(new AnswerWithArguments() {
public WifiConfiguration answer(String configKey) {
for (WifiConfiguration config : configs) {
if (TextUtils.equals(config.getProfileKey(), configKey)) {
return new WifiConfiguration(config);
}
}
return null;
}
});
when(wifiConfigManager.getConfiguredNetworks())
.then(new AnswerWithArguments() {
public List<WifiConfiguration> answer() {
List<WifiConfiguration> savedNetworks = new ArrayList<>();
for (int netId = 0; netId < configs.length; netId++) {
savedNetworks.add(new WifiConfiguration(configs[netId]));
}
return savedNetworks;
}
});
when(wifiConfigManager.clearNetworkCandidateScanResult(anyInt()))
.then(new AnswerWithArguments() {
public boolean answer(int netId) {
if (netId >= 0 && netId < configs.length) {
configs[netId].getNetworkSelectionStatus().setCandidate(null);
configs[netId].getNetworkSelectionStatus()
.setCandidateScore(Integer.MIN_VALUE);
configs[netId].getNetworkSelectionStatus()
.setCandidateSecurityParams(null);
configs[netId].getNetworkSelectionStatus()
.setSeenInLastQualifiedNetworkSelection(false);
return true;
} else {
return false;
}
}
});
when(wifiConfigManager.setNetworkCandidateScanResult(
anyInt(), any(ScanResult.class), anyInt(), any()))
.then(new AnswerWithArguments() {
public boolean answer(int netId, ScanResult scanResult, int score,
SecurityParams params) {
if (netId >= 0 && netId < configs.length) {
configs[netId].getNetworkSelectionStatus().setCandidate(scanResult);
configs[netId].getNetworkSelectionStatus().setCandidateScore(score);
configs[netId].getNetworkSelectionStatus()
.setCandidateSecurityParams(params);
configs[netId].getNetworkSelectionStatus()
.setSeenInLastQualifiedNetworkSelection(true);
return true;
} else {
return false;
}
}
});
}
/**
* Link scan results to the saved configurations.
*
* The shorter of the 2 input params will be used to loop over so the inputs don't
* need to be of equal length. If there are more scan details then configs the remaining scan
* details will be associated with a NULL config.
*
* @param wifiConfigManager the mocked WifiConfigManager
* @param configs saved configurations
* @param scanDetails come in scan results
*/
private static void scanResultLinkConfiguration(WifiConfigManager wifiConfigManager,
WifiConfiguration[] configs, List<ScanDetail> scanDetails) {
if (configs == null || scanDetails == null) {
return;
}
if (scanDetails.size() <= configs.length) {
for (int i = 0; i < scanDetails.size(); i++) {
ScanDetail scanDetail = scanDetails.get(i);
when(wifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetail)))
.thenReturn(configs[i]);
}
} else {
for (int i = 0; i < configs.length; i++) {
ScanDetail scanDetail = scanDetails.get(i);
when(wifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetail)))
.thenReturn(configs[i]);
}
// associated the remaining scan details with a NULL config.
for (int i = configs.length; i < scanDetails.size(); i++) {
when(wifiConfigManager.getSavedNetworkForScanDetailAndCache(
eq(scanDetails.get(i)))).thenReturn(null);
}
}
}
/**
* Configure the score cache for externally scored networks
*
* @param scoreCache Wifi network score cache to be configured
* @param scanDetails a list of ScanDetail
* @param scores scores of the networks
* @param meteredHints hints of if the networks are metered
*/
public static void configureScoreCache(WifiNetworkScoreCache scoreCache,
List<ScanDetail> scanDetails, Integer[] scores, boolean[] meteredHints) {
List<ScoredNetwork> networks = new ArrayList<>();
for (int i = 0; i < scanDetails.size(); i++) {
ScanDetail scanDetail = scanDetails.get(i);
ScanResult scanResult = scanDetail.getScanResult();
WifiKey wifiKey = new WifiKey("\"" + scanResult.SSID + "\"", scanResult.BSSID);
NetworkKey ntwkKey = new NetworkKey(wifiKey);
RssiCurve rssiCurve;
if (scores != null) { // fixed score
byte rssiScore;
Integer score = scores[i];
if (scores[i] == null) {
rssiScore = WifiNetworkScoreCache.INVALID_NETWORK_SCORE;
} else {
rssiScore = scores[i].byteValue();
}
rssiCurve = new RssiCurve(-100, 100, new byte[] {rssiScore});
} else {
rssiCurve = new RssiCurve(-80, 20, new byte[] {-10, 0, 10, 20, 30, 40});
}
ScoredNetwork scoredNetwork = new ScoredNetwork(ntwkKey, rssiCurve, meteredHints[i]);
networks.add(scoredNetwork);
}
scoreCache.onScoresUpdated(networks);
}
/**
* Setup WifiConfigManager mock for ephemeral networks.
*
* @param wifiConfigManager WifiConfigManager mock
* @param networkId ID of the ephemeral network
* @param scanDetail scanDetail of the ephemeral network
* @param meteredHint flag to indidate if the network has meteredHint
*/
public static WifiConfiguration setupEphemeralNetwork(WifiConfigManager wifiConfigManager,
int networkId, ScanDetail scanDetail, boolean meteredHint) {
// Return the correct networkID for ephemeral network addition.
when(wifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
.thenReturn(new NetworkUpdateResult(networkId));
final WifiConfiguration config =
ScanResultUtil.createNetworkFromScanResult(scanDetail.getScanResult());
config.ephemeral = true;
config.trusted = false;
config.networkId = networkId;
config.meteredHint = meteredHint;
when(wifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetail)))
.thenReturn(new WifiConfiguration(config));
when(wifiConfigManager.getConfiguredNetwork(eq(networkId)))
.then(new AnswerWithArguments() {
public WifiConfiguration answer(int netId) {
return new WifiConfiguration(config);
}
});
when(wifiConfigManager.setNetworkCandidateScanResult(
eq(networkId), any(ScanResult.class), anyInt(), any()))
.then(new AnswerWithArguments() {
public boolean answer(int netId, ScanResult scanResult, int score,
SecurityParams params) {
config.getNetworkSelectionStatus().setCandidate(scanResult);
config.getNetworkSelectionStatus().setCandidateScore(score);
config.getNetworkSelectionStatus()
.setCandidateSecurityParams(params);
config.getNetworkSelectionStatus()
.setSeenInLastQualifiedNetworkSelection(true);
return true;
}
});
when(wifiConfigManager.updateNetworkSelectionStatus(eq(networkId),
eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE)))
.then(new AnswerWithArguments() {
public boolean answer(int netId, int status) {
config.getNetworkSelectionStatus().setNetworkSelectionStatus(
WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE);
return true;
}
});
return config;
}
}