Home | History | Annotate | Download | only in wifi
      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 
     17 package com.android.server.wifi;
     18 
     19 import static com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig;
     20 
     21 import static org.junit.Assert.*;
     22 import static org.mockito.Mockito.*;
     23 
     24 import android.app.test.MockAnswerUtil.AnswerWithArguments;
     25 import android.net.NetworkKey;
     26 import android.net.RssiCurve;
     27 import android.net.ScoredNetwork;
     28 import android.net.WifiKey;
     29 import android.net.wifi.ScanResult;
     30 import android.net.wifi.WifiConfiguration;
     31 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
     32 import android.net.wifi.WifiNetworkScoreCache;
     33 import android.net.wifi.WifiSsid;
     34 import android.support.test.filters.SmallTest;
     35 import android.text.TextUtils;
     36 
     37 import com.android.server.wifi.util.ScanResultUtil;
     38 
     39 import java.util.ArrayList;
     40 import java.util.HashMap;
     41 import java.util.List;
     42 import java.util.Map;
     43 
     44 /**
     45  * Helper for WifiNetworkSelector unit tests.
     46  */
     47 @SmallTest
     48 public 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