1/* 2 * Copyright (C) 2016 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 17package com.android.server.wifi; 18 19import static com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig; 20 21import static org.junit.Assert.*; 22import static org.mockito.Mockito.*; 23 24import android.app.test.MockAnswerUtil.AnswerWithArguments; 25import android.net.NetworkKey; 26import android.net.RssiCurve; 27import android.net.ScoredNetwork; 28import android.net.WifiKey; 29import android.net.wifi.ScanResult; 30import android.net.wifi.WifiConfiguration; 31import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; 32import android.net.wifi.WifiNetworkScoreCache; 33import android.net.wifi.WifiSsid; 34import android.support.test.filters.SmallTest; 35import android.text.TextUtils; 36 37import com.android.server.wifi.util.ScanResultUtil; 38 39import java.util.ArrayList; 40import java.util.HashMap; 41import java.util.List; 42import java.util.Map; 43 44/** 45 * Helper for WifiNetworkSelector unit tests. 46 */ 47@SmallTest 48public class WifiNetworkSelectorTestUtil { 49 50 /** 51 * A class that holds a list of scanDetail and their associated WifiConfiguration. 52 */ 53 public static class ScanDetailsAndWifiConfigs { 54 List<ScanDetail> mScanDetails; 55 WifiConfiguration[] mWifiConfigs; 56 57 ScanDetailsAndWifiConfigs(List<ScanDetail> scanDetails, WifiConfiguration[] configs) { 58 mScanDetails = scanDetails; 59 mWifiConfigs = configs; 60 } 61 62 List<ScanDetail> getScanDetails() { 63 return mScanDetails; 64 } 65 66 WifiConfiguration[] getWifiConfigs() { 67 return mWifiConfigs; 68 } 69 } 70 71 /** 72 * Build a list of ScanDetail based on the caller supplied network SSID, BSSID, 73 * frequency, capability and RSSI level information. Create the corresponding 74 * WifiConfiguration for these networks and set up the mocked WifiConfigManager. 75 * 76 * @param ssids an array of SSIDs 77 * @param bssids an array of BSSIDs 78 * @param freqs an array of the network's frequency 79 * @param caps an array of the network's capability 80 * @param levels an array of the network's RSSI levels 81 * @param securities an array of the network's security setting 82 * @param wifiConfigManager the mocked WifiConfigManager 83 * @return the constructed ScanDetail list and WifiConfiguration array 84 */ 85 public static ScanDetailsAndWifiConfigs setupScanDetailsAndConfigStore(String[] ssids, 86 String[] bssids, int[] freqs, String[] caps, int[] levels, int[] securities, 87 WifiConfigManager wifiConfigManager, Clock clock) { 88 List<ScanDetail> scanDetails = buildScanDetails(ssids, bssids, freqs, caps, levels, clock); 89 WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, securities); 90 prepareConfigStore(wifiConfigManager, savedConfigs); 91 scanResultLinkConfiguration(wifiConfigManager, savedConfigs, scanDetails); 92 93 return new ScanDetailsAndWifiConfigs(scanDetails, savedConfigs); 94 } 95 96 /** 97 * Verify whether the WifiConfiguration chosen by WifiNetworkSelector matches 98 * with the chosen scan result. 99 * 100 * @param chosenScanResult the chosen scan result 101 * @param chosenCandidate the chosen configuration 102 */ 103 public static void verifySelectedScanResult(WifiConfigManager wifiConfigManager, 104 ScanResult chosenScanResult, WifiConfiguration chosenCandidate) { 105 verify(wifiConfigManager, atLeastOnce()).setNetworkCandidateScanResult( 106 eq(chosenCandidate.networkId), eq(chosenScanResult), anyInt()); 107 } 108 109 110 /** 111 * Build a list of scanDetails based on the caller supplied network SSID, BSSID, 112 * frequency, capability and RSSI level information. 113 * 114 * @param ssids an array of SSIDs 115 * @param bssids an array of BSSIDs 116 * @param freqs an array of the network's frequency 117 * @param caps an array of the network's capability 118 * @param levels an array of the network's RSSI levels 119 * @return the constructed list of ScanDetail 120 */ 121 public static List<ScanDetail> buildScanDetails(String[] ssids, String[] bssids, int[] freqs, 122 String[] caps, int[] levels, Clock clock) { 123 List<ScanDetail> scanDetailList = new ArrayList<ScanDetail>(); 124 125 long timeStamp = clock.getElapsedSinceBootMillis(); 126 for (int index = 0; index < ssids.length; index++) { 127 ScanDetail scanDetail = new ScanDetail(WifiSsid.createFromAsciiEncoded(ssids[index]), 128 bssids[index], caps[index], levels[index], freqs[index], timeStamp, 0); 129 scanDetailList.add(scanDetail); 130 } 131 return scanDetailList; 132 } 133 134 135 /** 136 * Generate an array of {@link android.net.wifi.WifiConfiguration} based on the caller 137 * supplied network SSID and security information. 138 * 139 * @param ssids an array of SSIDs 140 * @param securities an array of the network's security setting 141 * @return the constructed array of {@link android.net.wifi.WifiConfiguration} 142 */ 143 public static WifiConfiguration[] generateWifiConfigurations(String[] ssids, 144 int[] securities) { 145 if (ssids == null || securities == null || ssids.length != securities.length 146 || ssids.length == 0) { 147 return null; 148 } 149 150 Map<String, Integer> netIdMap = new HashMap<>(); 151 int netId = 0; 152 153 WifiConfiguration[] configs = new WifiConfiguration[ssids.length]; 154 for (int index = 0; index < ssids.length; index++) { 155 String configKey = ssids[index] + Integer.toString(securities[index]); 156 Integer id; 157 158 id = netIdMap.get(configKey); 159 if (id == null) { 160 id = new Integer(netId); 161 netIdMap.put(configKey, id); 162 netId++; 163 } 164 165 configs[index] = generateWifiConfig(id.intValue(), 0, ssids[index], false, true, null, 166 null, securities[index]); 167 } 168 169 return configs; 170 } 171 172 /** 173 * Add the Configurations to WifiConfigManager (WifiConfigureStore can take them out according 174 * to the networkd ID) and setup the WifiConfigManager mocks for these networks. 175 * This simulates the WifiConfigManager class behaviour. 176 * 177 * @param wifiConfigManager the mocked WifiConfigManager 178 * @param configs input configuration need to be added to WifiConfigureStore 179 */ 180 private static void prepareConfigStore(final WifiConfigManager wifiConfigManager, 181 final WifiConfiguration[] configs) { 182 when(wifiConfigManager.getConfiguredNetwork(anyInt())) 183 .then(new AnswerWithArguments() { 184 public WifiConfiguration answer(int netId) { 185 for (WifiConfiguration config : configs) { 186 if (netId == config.networkId) { 187 return new WifiConfiguration(config); 188 } 189 } 190 return null; 191 } 192 }); 193 when(wifiConfigManager.getConfiguredNetwork(anyString())) 194 .then(new AnswerWithArguments() { 195 public WifiConfiguration answer(String configKey) { 196 for (WifiConfiguration config : configs) { 197 if (TextUtils.equals(config.configKey(), configKey)) { 198 return new WifiConfiguration(config); 199 } 200 } 201 return null; 202 } 203 }); 204 when(wifiConfigManager.getSavedNetworks()) 205 .then(new AnswerWithArguments() { 206 public List<WifiConfiguration> answer() { 207 List<WifiConfiguration> savedNetworks = new ArrayList<>(); 208 for (int netId = 0; netId < configs.length; netId++) { 209 savedNetworks.add(new WifiConfiguration(configs[netId])); 210 } 211 return savedNetworks; 212 } 213 }); 214 when(wifiConfigManager.clearNetworkCandidateScanResult(anyInt())) 215 .then(new AnswerWithArguments() { 216 public boolean answer(int netId) { 217 if (netId >= 0 && netId < configs.length) { 218 configs[netId].getNetworkSelectionStatus().setCandidate(null); 219 configs[netId].getNetworkSelectionStatus() 220 .setCandidateScore(Integer.MIN_VALUE); 221 configs[netId].getNetworkSelectionStatus() 222 .setSeenInLastQualifiedNetworkSelection(false); 223 return true; 224 } else { 225 return false; 226 } 227 } 228 }); 229 when(wifiConfigManager.setNetworkCandidateScanResult( 230 anyInt(), any(ScanResult.class), anyInt())) 231 .then(new AnswerWithArguments() { 232 public boolean answer(int netId, ScanResult scanResult, int score) { 233 if (netId >= 0 && netId < configs.length) { 234 configs[netId].getNetworkSelectionStatus().setCandidate(scanResult); 235 configs[netId].getNetworkSelectionStatus().setCandidateScore(score); 236 configs[netId].getNetworkSelectionStatus() 237 .setSeenInLastQualifiedNetworkSelection(true); 238 return true; 239 } else { 240 return false; 241 } 242 } 243 }); 244 when(wifiConfigManager.clearNetworkConnectChoice(anyInt())) 245 .then(new AnswerWithArguments() { 246 public boolean answer(int netId) { 247 if (netId >= 0 && netId < configs.length) { 248 configs[netId].getNetworkSelectionStatus().setConnectChoice(null); 249 configs[netId].getNetworkSelectionStatus() 250 .setConnectChoiceTimestamp( 251 NetworkSelectionStatus 252 .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); 253 return true; 254 } else { 255 return false; 256 } 257 } 258 }); 259 when(wifiConfigManager.setNetworkConnectChoice(anyInt(), anyString(), anyLong())) 260 .then(new AnswerWithArguments() { 261 public boolean answer(int netId, String configKey, long timestamp) { 262 if (netId >= 0 && netId < configs.length) { 263 configs[netId].getNetworkSelectionStatus().setConnectChoice(configKey); 264 configs[netId].getNetworkSelectionStatus().setConnectChoiceTimestamp( 265 timestamp); 266 return true; 267 } else { 268 return false; 269 } 270 } 271 }); 272 } 273 274 275 /** 276 * Link scan results to the saved configurations. 277 * 278 * The shorter of the 2 input params will be used to loop over so the inputs don't 279 * need to be of equal length. If there are more scan details then configs the remaining scan 280 * details will be associated with a NULL config. 281 * 282 * @param wifiConfigManager the mocked WifiConfigManager 283 * @param configs saved configurations 284 * @param scanDetails come in scan results 285 */ 286 private static void scanResultLinkConfiguration(WifiConfigManager wifiConfigManager, 287 WifiConfiguration[] configs, List<ScanDetail> scanDetails) { 288 if (configs == null || scanDetails == null) { 289 return; 290 } 291 292 if (scanDetails.size() <= configs.length) { 293 for (int i = 0; i < scanDetails.size(); i++) { 294 ScanDetail scanDetail = scanDetails.get(i); 295 when(wifiConfigManager.getConfiguredNetworkForScanDetailAndCache(eq(scanDetail))) 296 .thenReturn(configs[i]); 297 } 298 } else { 299 for (int i = 0; i < configs.length; i++) { 300 ScanDetail scanDetail = scanDetails.get(i); 301 when(wifiConfigManager.getConfiguredNetworkForScanDetailAndCache(eq(scanDetail))) 302 .thenReturn(configs[i]); 303 } 304 305 // associated the remaining scan details with a NULL config. 306 for (int i = configs.length; i < scanDetails.size(); i++) { 307 when(wifiConfigManager.getConfiguredNetworkForScanDetailAndCache( 308 eq(scanDetails.get(i)))).thenReturn(null); 309 } 310 } 311 } 312 313 314 /** 315 * Configure the score cache for externally scored networks 316 * 317 * @param scoreCache Wifi network score cache to be configured 318 * @param scanDetails a list of ScanDetail 319 * @param scores scores of the networks 320 * @param meteredHints hints of if the networks are metered 321 */ 322 public static void configureScoreCache(WifiNetworkScoreCache scoreCache, 323 List<ScanDetail> scanDetails, Integer[] scores, boolean[] meteredHints) { 324 List<ScoredNetwork> networks = new ArrayList<>(); 325 326 for (int i = 0; i < scanDetails.size(); i++) { 327 ScanDetail scanDetail = scanDetails.get(i); 328 ScanResult scanResult = scanDetail.getScanResult(); 329 WifiKey wifiKey = new WifiKey("\"" + scanResult.SSID + "\"", scanResult.BSSID); 330 NetworkKey ntwkKey = new NetworkKey(wifiKey); 331 RssiCurve rssiCurve; 332 333 if (scores != null) { // fixed score 334 byte rssiScore; 335 Integer score = scores[i]; 336 337 if (scores[i] == null) { 338 rssiScore = WifiNetworkScoreCache.INVALID_NETWORK_SCORE; 339 } else { 340 rssiScore = scores[i].byteValue(); 341 } 342 rssiCurve = new RssiCurve(-100, 100, new byte[] {rssiScore}); 343 } else { 344 rssiCurve = new RssiCurve(-80, 20, new byte[] {-10, 0, 10, 20, 30, 40}); 345 } 346 ScoredNetwork scoredNetwork = new ScoredNetwork(ntwkKey, rssiCurve, meteredHints[i]); 347 348 networks.add(scoredNetwork); 349 } 350 351 scoreCache.updateScores(networks); 352 } 353 354 /** 355 * Setup WifiConfigManager mock for ephemeral networks. 356 * 357 * @param wifiConfigManager WifiConfigManager mock 358 * @param networkId ID of the ephemeral network 359 * @param scanDetail scanDetail of the ephemeral network 360 * @param meteredHint flag to indidate if the network has meteredHint 361 */ 362 public static WifiConfiguration setupEphemeralNetwork(WifiConfigManager wifiConfigManager, 363 int networkId, ScanDetail scanDetail, boolean meteredHint) { 364 // Return the correct networkID for ephemeral network addition. 365 when(wifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt())) 366 .thenReturn(new NetworkUpdateResult(networkId)); 367 final WifiConfiguration config = 368 ScanResultUtil.createNetworkFromScanResult(scanDetail.getScanResult()); 369 config.ephemeral = true; 370 config.networkId = networkId; 371 config.meteredHint = meteredHint; 372 373 when(wifiConfigManager.getConfiguredNetworkForScanDetailAndCache(eq(scanDetail))) 374 .thenReturn(new WifiConfiguration(config)); 375 when(wifiConfigManager.getConfiguredNetwork(eq(networkId))) 376 .then(new AnswerWithArguments() { 377 public WifiConfiguration answer(int netId) { 378 return new WifiConfiguration(config); 379 } 380 }); 381 when(wifiConfigManager.setNetworkCandidateScanResult( 382 eq(networkId), any(ScanResult.class), anyInt())) 383 .then(new AnswerWithArguments() { 384 public boolean answer(int netId, ScanResult scanResult, int score) { 385 config.getNetworkSelectionStatus().setCandidate(scanResult); 386 config.getNetworkSelectionStatus().setCandidateScore(score); 387 config.getNetworkSelectionStatus() 388 .setSeenInLastQualifiedNetworkSelection(true); 389 return true; 390 } 391 }); 392 when(wifiConfigManager.updateNetworkSelectionStatus(eq(networkId), 393 eq(WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE))) 394 .then(new AnswerWithArguments() { 395 public boolean answer(int netId, int status) { 396 config.getNetworkSelectionStatus().setNetworkSelectionStatus( 397 WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); 398 return true; 399 } 400 }); 401 return config; 402 } 403} 404