Home | History | Annotate | Download | only in wifi
      1 #!/usr/bin/env python3.4
      2 #
      3 #   Copyright 2016 Google, Inc.
      4 #
      5 #   Licensed under the Apache License, Version 2.0 (the "License");
      6 #   you may not use this file except in compliance with the License.
      7 #   You may obtain a copy of the License at
      8 #
      9 #       http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 #   Unless required by applicable law or agreed to in writing, software
     12 #   distributed under the License is distributed on an "AS IS" BASIS,
     13 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 #   See the License for the specific language governing permissions and
     15 #   limitations under the License.
     16 
     17 import logging
     18 import time
     19 import pprint
     20 
     21 from enum import IntEnum
     22 from queue import Empty
     23 
     24 from acts import asserts
     25 from acts import signals
     26 from acts import utils
     27 from acts.controllers import attenuator
     28 from acts.test_utils.wifi import wifi_constants
     29 from acts.test_utils.tel import tel_defines
     30 
     31 # Default timeout used for reboot, toggle WiFi and Airplane mode,
     32 # for the system to settle down after the operation.
     33 DEFAULT_TIMEOUT = 10
     34 # Number of seconds to wait for events that are supposed to happen quickly.
     35 # Like onSuccess for start background scan and confirmation on wifi state
     36 # change.
     37 SHORT_TIMEOUT = 30
     38 
     39 # The currently supported devices that existed before release
     40 #TODO: (navtejsingh) Need to clean up the below lists going forward
     41 K_DEVICES = ["hammerhead", "razor", "razorg"]
     42 L_DEVICES = ["shamu", "ryu"]
     43 L_TAP_DEVICES = ["volantis", "volantisg"]
     44 M_DEVICES = ["angler"]
     45 
     46 # Speed of light in m/s.
     47 SPEED_OF_LIGHT = 299792458
     48 
     49 DEFAULT_PING_ADDR = "http://www.google.com/robots.txt"
     50 
     51 
     52 class WifiEnums():
     53 
     54     SSID_KEY = "SSID"
     55     NETID_KEY = "network_id"
     56     BSSID_KEY = "BSSID"
     57     PWD_KEY = "password"
     58     frequency_key = "frequency"
     59     APBAND_KEY = "apBand"
     60 
     61     WIFI_CONFIG_APBAND_2G = 0
     62     WIFI_CONFIG_APBAND_5G = 1
     63 
     64     WIFI_WPS_INFO_PBC = 0
     65     WIFI_WPS_INFO_DISPLAY = 1
     66     WIFI_WPS_INFO_KEYPAD = 2
     67     WIFI_WPS_INFO_LABEL = 3
     68     WIFI_WPS_INFO_INVALID = 4
     69 
     70     class CountryCode():
     71         CHINA = "CN"
     72         JAPAN = "JP"
     73         UK = "GB"
     74         US = "US"
     75         UNKNOWN = "UNKNOWN"
     76 
     77     # Start of Macros for EAP
     78     # EAP types
     79     class Eap(IntEnum):
     80         NONE = -1
     81         PEAP = 0
     82         TLS = 1
     83         TTLS = 2
     84         PWD = 3
     85         SIM = 4
     86         AKA = 5
     87         AKA_PRIME = 6
     88         UNAUTH_TLS = 7
     89 
     90     # EAP Phase2 types
     91     class EapPhase2(IntEnum):
     92         NONE = 0
     93         PAP = 1
     94         MSCHAP = 2
     95         MSCHAPV2 = 3
     96         GTC = 4
     97 
     98     class Enterprise:
     99         # Enterprise Config Macros
    100         EMPTY_VALUE = "NULL"
    101         EAP = "eap"
    102         PHASE2 = "phase2"
    103         IDENTITY = "identity"
    104         ANON_IDENTITY = "anonymous_identity"
    105         PASSWORD = "password"
    106         SUBJECT_MATCH = "subject_match"
    107         ALTSUBJECT_MATCH = "altsubject_match"
    108         DOM_SUFFIX_MATCH = "domain_suffix_match"
    109         CLIENT_CERT = "client_cert"
    110         CA_CERT = "ca_cert"
    111         ENGINE = "engine"
    112         ENGINE_ID = "engine_id"
    113         PRIVATE_KEY_ID = "key_id"
    114         REALM = "realm"
    115         PLMN = "plmn"
    116         FQDN = "FQDN"
    117         FRIENDLY_NAME = "providerFriendlyName"
    118         ROAMING_IDS = "roamingConsortiumIds"
    119     # End of Macros for EAP
    120 
    121     # Macros for wifi p2p.
    122     WIFI_P2P_SERVICE_TYPE_ALL = 0
    123     WIFI_P2P_SERVICE_TYPE_BONJOUR = 1
    124     WIFI_P2P_SERVICE_TYPE_UPNP = 2
    125     WIFI_P2P_SERVICE_TYPE_VENDOR_SPECIFIC = 255
    126 
    127     class ScanResult:
    128         CHANNEL_WIDTH_20MHZ = 0
    129         CHANNEL_WIDTH_40MHZ = 1
    130         CHANNEL_WIDTH_80MHZ = 2
    131         CHANNEL_WIDTH_160MHZ = 3
    132         CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4
    133 
    134     # Macros for wifi rtt.
    135     class RttType(IntEnum):
    136         TYPE_ONE_SIDED = 1
    137         TYPE_TWO_SIDED = 2
    138 
    139     class RttPeerType(IntEnum):
    140         PEER_TYPE_AP = 1
    141         PEER_TYPE_STA = 2  # Requires NAN.
    142         PEER_P2P_GO = 3
    143         PEER_P2P_CLIENT = 4
    144         PEER_NAN = 5
    145 
    146     class RttPreamble(IntEnum):
    147         PREAMBLE_LEGACY = 0x01
    148         PREAMBLE_HT = 0x02
    149         PREAMBLE_VHT = 0x04
    150 
    151     class RttBW(IntEnum):
    152         BW_5_SUPPORT = 0x01
    153         BW_10_SUPPORT = 0x02
    154         BW_20_SUPPORT = 0x04
    155         BW_40_SUPPORT = 0x08
    156         BW_80_SUPPORT = 0x10
    157         BW_160_SUPPORT = 0x20
    158 
    159     class Rtt(IntEnum):
    160         STATUS_SUCCESS = 0
    161         STATUS_FAILURE = 1
    162         STATUS_FAIL_NO_RSP = 2
    163         STATUS_FAIL_REJECTED = 3
    164         STATUS_FAIL_NOT_SCHEDULED_YET = 4
    165         STATUS_FAIL_TM_TIMEOUT = 5
    166         STATUS_FAIL_AP_ON_DIFF_CHANNEL = 6
    167         STATUS_FAIL_NO_CAPABILITY = 7
    168         STATUS_ABORTED = 8
    169         STATUS_FAIL_INVALID_TS = 9
    170         STATUS_FAIL_PROTOCOL = 10
    171         STATUS_FAIL_SCHEDULE = 11
    172         STATUS_FAIL_BUSY_TRY_LATER = 12
    173         STATUS_INVALID_REQ = 13
    174         STATUS_NO_WIFI = 14
    175         STATUS_FAIL_FTM_PARAM_OVERRIDE = 15
    176 
    177         REASON_UNSPECIFIED = -1
    178         REASON_NOT_AVAILABLE = -2
    179         REASON_INVALID_LISTENER = -3
    180         REASON_INVALID_REQUEST = -4
    181 
    182     class RttParam:
    183         device_type = "deviceType"
    184         request_type = "requestType"
    185         BSSID = "bssid"
    186         channel_width = "channelWidth"
    187         frequency = "frequency"
    188         center_freq0 = "centerFreq0"
    189         center_freq1 = "centerFreq1"
    190         number_burst = "numberBurst"
    191         interval = "interval"
    192         num_samples_per_burst = "numSamplesPerBurst"
    193         num_retries_per_measurement_frame = "numRetriesPerMeasurementFrame"
    194         num_retries_per_FTMR = "numRetriesPerFTMR"
    195         lci_request = "LCIRequest"
    196         lcr_request = "LCRRequest"
    197         burst_timeout = "burstTimeout"
    198         preamble = "preamble"
    199         bandwidth = "bandwidth"
    200         margin = "margin"
    201 
    202     RTT_MARGIN_OF_ERROR = {
    203         RttBW.BW_80_SUPPORT: 2,
    204         RttBW.BW_40_SUPPORT: 5,
    205         RttBW.BW_20_SUPPORT: 5
    206     }
    207 
    208     # Macros as specified in the WifiScanner code.
    209     WIFI_BAND_UNSPECIFIED = 0  # not specified
    210     WIFI_BAND_24_GHZ = 1  # 2.4 GHz band
    211     WIFI_BAND_5_GHZ = 2  # 5 GHz band without DFS channels
    212     WIFI_BAND_5_GHZ_DFS_ONLY = 4  # 5 GHz band with DFS channels
    213     WIFI_BAND_5_GHZ_WITH_DFS = 6  # 5 GHz band with DFS channels
    214     WIFI_BAND_BOTH = 3  # both bands without DFS channels
    215     WIFI_BAND_BOTH_WITH_DFS = 7  # both bands with DFS channels
    216 
    217     REPORT_EVENT_AFTER_BUFFER_FULL = 0
    218     REPORT_EVENT_AFTER_EACH_SCAN = 1
    219     REPORT_EVENT_FULL_SCAN_RESULT = 2
    220 
    221     # US Wifi frequencies
    222     ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
    223                           2457, 2462]
    224     DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560, 5580,
    225                           5600, 5620, 5640, 5660, 5680, 5700, 5720]
    226     NONE_DFS_5G_FREQUENCIES = [5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805,
    227                                5825]
    228     ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
    229 
    230     band_to_frequencies = {
    231         WIFI_BAND_24_GHZ: ALL_2G_FREQUENCIES,
    232         WIFI_BAND_5_GHZ: NONE_DFS_5G_FREQUENCIES,
    233         WIFI_BAND_5_GHZ_DFS_ONLY: DFS_5G_FREQUENCIES,
    234         WIFI_BAND_5_GHZ_WITH_DFS: ALL_5G_FREQUENCIES,
    235         WIFI_BAND_BOTH: ALL_2G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES,
    236         WIFI_BAND_BOTH_WITH_DFS: ALL_5G_FREQUENCIES + ALL_2G_FREQUENCIES
    237     }
    238 
    239     # All Wifi frequencies to channels lookup.
    240     freq_to_channel = {
    241         2412: 1,
    242         2417: 2,
    243         2422: 3,
    244         2427: 4,
    245         2432: 5,
    246         2437: 6,
    247         2442: 7,
    248         2447: 8,
    249         2452: 9,
    250         2457: 10,
    251         2462: 11,
    252         2467: 12,
    253         2472: 13,
    254         2484: 14,
    255         4915: 183,
    256         4920: 184,
    257         4925: 185,
    258         4935: 187,
    259         4940: 188,
    260         4945: 189,
    261         4960: 192,
    262         4980: 196,
    263         5035: 7,
    264         5040: 8,
    265         5045: 9,
    266         5055: 11,
    267         5060: 12,
    268         5080: 16,
    269         5170: 34,
    270         5180: 36,
    271         5190: 38,
    272         5200: 40,
    273         5210: 42,
    274         5220: 44,
    275         5230: 46,
    276         5240: 48,
    277         5260: 52,
    278         5280: 56,
    279         5300: 60,
    280         5320: 64,
    281         5500: 100,
    282         5520: 104,
    283         5540: 108,
    284         5560: 112,
    285         5580: 116,
    286         5600: 120,
    287         5620: 124,
    288         5640: 128,
    289         5660: 132,
    290         5680: 136,
    291         5700: 140,
    292         5745: 149,
    293         5765: 153,
    294         5785: 157,
    295         5805: 161,
    296         5825: 165,
    297     }
    298 
    299     # All Wifi channels to frequencies lookup.
    300     channel_2G_to_freq = {
    301         1: 2412,
    302         2: 2417,
    303         3: 2422,
    304         4: 2427,
    305         5: 2432,
    306         6: 2437,
    307         7: 2442,
    308         8: 2447,
    309         9: 2452,
    310         10: 2457,
    311         11: 2462,
    312         12: 2467,
    313         13: 2472,
    314         14: 2484
    315     }
    316 
    317     channel_5G_to_freq = {
    318         183: 4915,
    319         184: 4920,
    320         185: 4925,
    321         187: 4935,
    322         188: 4940,
    323         189: 4945,
    324         192: 4960,
    325         196: 4980,
    326         7: 5035,
    327         8: 5040,
    328         9: 5045,
    329         11: 5055,
    330         12: 5060,
    331         16: 5080,
    332         34: 5170,
    333         36: 5180,
    334         38: 5190,
    335         40: 5200,
    336         42: 5210,
    337         44: 5220,
    338         46: 5230,
    339         48: 5240,
    340         52: 5260,
    341         56: 5280,
    342         60: 5300,
    343         64: 5320,
    344         100: 5500,
    345         104: 5520,
    346         108: 5540,
    347         112: 5560,
    348         116: 5580,
    349         120: 5600,
    350         124: 5620,
    351         128: 5640,
    352         132: 5660,
    353         136: 5680,
    354         140: 5700,
    355         149: 5745,
    356         153: 5765,
    357         157: 5785,
    358         161: 5805,
    359         165: 5825
    360     }
    361 
    362 
    363 class WifiChannelBase:
    364     ALL_2G_FREQUENCIES = []
    365     DFS_5G_FREQUENCIES = []
    366     NONE_DFS_5G_FREQUENCIES = []
    367     ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
    368     MIX_CHANNEL_SCAN = []
    369 
    370     def band_to_freq(self, band):
    371         _band_to_frequencies = {
    372             WifiEnums.WIFI_BAND_24_GHZ: self.ALL_2G_FREQUENCIES,
    373             WifiEnums.WIFI_BAND_5_GHZ: self.NONE_DFS_5G_FREQUENCIES,
    374             WifiEnums.WIFI_BAND_5_GHZ_DFS_ONLY: self.DFS_5G_FREQUENCIES,
    375             WifiEnums.WIFI_BAND_5_GHZ_WITH_DFS: self.ALL_5G_FREQUENCIES,
    376             WifiEnums.WIFI_BAND_BOTH:
    377             self.ALL_2G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES,
    378             WifiEnums.WIFI_BAND_BOTH_WITH_DFS:
    379             self.ALL_5G_FREQUENCIES + self.ALL_2G_FREQUENCIES
    380         }
    381         return _band_to_frequencies[band]
    382 
    383 
    384 class WifiChannelUS(WifiChannelBase):
    385     # US Wifi frequencies
    386     ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
    387                           2457, 2462]
    388     NONE_DFS_5G_FREQUENCIES = [5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805,
    389                                5825]
    390     MIX_CHANNEL_SCAN = [2412, 2437, 2462, 5180, 5200, 5280, 5260, 5300, 5500,
    391                         5320, 5520, 5560, 5700, 5745, 5805]
    392 
    393     def __init__(self, model=None):
    394         if model and utils.trim_model_name(model) in K_DEVICES:
    395             self.DFS_5G_FREQUENCIES = []
    396             self.ALL_5G_FREQUENCIES = self.NONE_DFS_5G_FREQUENCIES
    397             self.MIX_CHANNEL_SCAN = [2412, 2437, 2462, 5180, 5200, 5240, 5745,
    398                                      5765]
    399         elif model and utils.trim_model_name(model) in L_DEVICES:
    400             self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520,
    401                                        5540, 5560, 5580, 5660, 5680, 5700]
    402             self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
    403         elif model and utils.trim_model_name(model) in L_TAP_DEVICES:
    404             self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520,
    405                                        5540, 5560, 5580, 5660, 5680, 5700,
    406                                        5720]
    407             self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
    408         elif model and utils.trim_model_name(model) in M_DEVICES:
    409             self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520,
    410                                        5540, 5560, 5580, 5600, 5620, 5640,
    411                                        5660, 5680, 5700]
    412             self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
    413         else:
    414             self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520,
    415                                        5540, 5560, 5580, 5600, 5620, 5640,
    416                                        5660, 5680, 5700, 5720]
    417             self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
    418 
    419 class WifiReferenceNetworks:
    420     """ Class to parse and return networks of different band and
    421         auth type from reference_networks
    422     """
    423     def __init__(self, obj):
    424         self.reference_networks = obj
    425         self.WIFI_2G = "2g"
    426         self.WIFI_5G = "5g"
    427 
    428         self.secure_networks_2g = []
    429         self.secure_networks_5g = []
    430         self.open_networks_2g = []
    431         self.open_networks_5g = []
    432         self._parse_networks()
    433 
    434     def _parse_networks(self):
    435         for network in self.reference_networks:
    436             for key in network:
    437                 if key == self.WIFI_2G:
    438                     if "password" in network[key]:
    439                         self.secure_networks_2g.append(network[key])
    440                     else:
    441                         self.open_networks_2g.append(network[key])
    442                 else:
    443                     if "password" in network[key]:
    444                         self.secure_networks_5g.append(network[key])
    445                     else:
    446                         self.open_networks_5g.append(network[key])
    447 
    448     def return_2g_secure_networks(self):
    449         return self.secure_networks_2g
    450 
    451     def return_5g_secure_networks(self):
    452         return self.secure_networks_5g
    453 
    454     def return_2g_open_networks(self):
    455         return self.open_networks_2g
    456 
    457     def return_5g_open_networks(self):
    458         return self.open_networks_5g
    459 
    460     def return_secure_networks(self):
    461         return self.secure_networks_2g + self.secure_networks_5g
    462 
    463     def return_open_networks(self):
    464         return self.open_networks_2g + self.open_networks_5g
    465 
    466 
    467 def _assert_on_fail_handler(func, assert_on_fail, *args, **kwargs):
    468     """Wrapper function that handles the bahevior of assert_on_fail.
    469 
    470     When assert_on_fail is True, let all test signals through, which can
    471     terminate test cases directly. When assert_on_fail is False, the wrapper
    472     raises no test signals and reports operation status by returning True or
    473     False.
    474 
    475     Args:
    476         func: The function to wrap. This function reports operation status by
    477               raising test signals.
    478         assert_on_fail: A boolean that specifies if the output of the wrapper
    479                         is test signal based or return value based.
    480         args: Positional args for func.
    481         kwargs: Name args for func.
    482 
    483     Returns:
    484         If assert_on_fail is True, returns True/False to signal operation
    485         status, otherwise return nothing.
    486     """
    487     try:
    488         func(*args, **kwargs)
    489         if not assert_on_fail:
    490             return True
    491     except signals.TestSignal:
    492         if assert_on_fail:
    493             raise
    494         return False
    495 
    496 
    497 def assert_network_in_list(target, network_list):
    498     """Makes sure a specified target Wi-Fi network exists in a list of Wi-Fi
    499     networks.
    500 
    501     Args:
    502         target: A dict representing a Wi-Fi network.
    503                 E.g. {WifiEnums.SSID_KEY: "SomeNetwork"}
    504         network_list: A list of dicts, each representing a Wi-Fi network.
    505     """
    506     match_results = match_networks(target, network_list)
    507     asserts.assert_true(
    508         match_results, "Target network %s, does not exist in network list %s" %
    509         (target, network_list))
    510 
    511 
    512 def match_networks(target_params, networks):
    513     """Finds the WiFi networks that match a given set of parameters in a list
    514     of WiFi networks.
    515 
    516     To be considered a match, the network should contain every key-value pair
    517     of target_params
    518 
    519     Args:
    520         target_params: A dict with 1 or more key-value pairs representing a Wi-Fi network.
    521                        E.g { 'SSID': 'wh_ap1_5g', 'BSSID': '30:b5:c2:33:e4:47' }
    522         networks: A list of dict objects representing WiFi networks.
    523 
    524     Returns:
    525         The networks that match the target parameters.
    526     """
    527     results = []
    528     asserts.assert_true(target_params,
    529                         "Expected networks object 'target_params' is empty")
    530     for n in networks:
    531         add_network = 1
    532         for k, v in target_params.items():
    533             if k not in n:
    534                 add_network = 0
    535                 break
    536             if n[k] != v:
    537                 add_network = 0
    538                 break
    539         if add_network:
    540             results.append(n)
    541     return results
    542 
    543 
    544 def wifi_toggle_state(ad, new_state=None, assert_on_fail=True):
    545     """Toggles the state of wifi.
    546 
    547     Args:
    548         ad: An AndroidDevice object.
    549         new_state: Wifi state to set to. If None, opposite of the current state.
    550         assert_on_fail: If True, error checks in this function will raise test
    551                         failure signals.
    552 
    553     Returns:
    554         If assert_on_fail is False, function returns True if the toggle was
    555         successful, False otherwise. If assert_on_fail is True, no return value.
    556     """
    557     return _assert_on_fail_handler(
    558         _wifi_toggle_state, assert_on_fail, ad, new_state=new_state)
    559 
    560 
    561 def _wifi_toggle_state(ad, new_state=None):
    562     """Toggles the state of wifi.
    563 
    564     TestFailure signals are raised when something goes wrong.
    565 
    566     Args:
    567         ad: An AndroidDevice object.
    568         new_state: The state to set Wi-Fi to. If None, opposite of the current
    569                    state will be set.
    570     """
    571     if new_state is None:
    572         new_state = not ad.droid.wifiCheckState()
    573     elif new_state == ad.droid.wifiCheckState():
    574         # Check if the new_state is already achieved, so we don't wait for the
    575         # state change event by mistake.
    576         return
    577     ad.droid.wifiStartTrackingStateChange()
    578     ad.log.info("Setting Wi-Fi state to %s.", new_state)
    579     # Setting wifi state.
    580     ad.droid.wifiToggleState(new_state)
    581     fail_msg = "Failed to set Wi-Fi state to %s on %s." % (new_state,
    582                                                            ad.serial)
    583     try:
    584         event = ad.ed.pop_event(wifi_constants.SUPPLICANT_CON_CHANGED,
    585                                 SHORT_TIMEOUT)
    586         asserts.assert_equal(event['data']['Connected'], new_state, fail_msg)
    587     except Empty:
    588         # Supplicant connection event is not always reliable. We double check
    589         # here and call it a success as long as the new state equals the
    590         # expected state.
    591         time.sleep(5)
    592         asserts.assert_equal(new_state, ad.droid.wifiCheckState(), fail_msg)
    593     finally:
    594         ad.droid.wifiStopTrackingStateChange()
    595 
    596 
    597 def reset_wifi(ad):
    598     """Clears all saved Wi-Fi networks on a device.
    599 
    600     This will turn Wi-Fi on.
    601 
    602     Args:
    603         ad: An AndroidDevice object.
    604 
    605     """
    606     # TODO(gmoturu): need to remove wifi_toggle_state() in reset_wifi() when
    607     # bug: 32809235 is fixed
    608     wifi_toggle_state(ad, True)
    609     networks = ad.droid.wifiGetConfiguredNetworks()
    610     if not networks:
    611         return
    612     for n in networks:
    613         ad.droid.wifiForgetNetwork(n['networkId'])
    614         try:
    615             event = ad.ed.pop_event(wifi_constants.WIFI_FORGET_NW_SUCCESS,
    616                                     SHORT_TIMEOUT)
    617         except Empty:
    618             logging.warning("Could not confirm the removal of network %s.", n)
    619     # Check again to see if there's any network left.
    620     asserts.assert_true(
    621         not ad.droid.wifiGetConfiguredNetworks(),
    622         "Failed to remove these configured Wi-Fi networks: %s" % networks)
    623 
    624 
    625 def toggle_airplane_mode_on_and_off(ad):
    626     """Turn ON and OFF Airplane mode.
    627 
    628     ad: An AndroidDevice object.
    629     Returns: Assert if turning on/off Airplane mode fails.
    630 
    631     """
    632     ad.log.debug("Toggling Airplane mode ON.")
    633     asserts.assert_true(
    634         utils.force_airplane_mode(ad, True),
    635         "Can not turn on airplane mode on: %s" % ad.serial)
    636     time.sleep(DEFAULT_TIMEOUT)
    637     ad.log.debug("Toggling Airplane mode OFF.")
    638     asserts.assert_true(
    639         utils.force_airplane_mode(ad, False),
    640         "Can not turn on airplane mode on: %s" % ad.serial)
    641     time.sleep(DEFAULT_TIMEOUT)
    642 
    643 
    644 def toggle_wifi_off_and_on(ad):
    645     """Turn OFF and ON WiFi.
    646 
    647     ad: An AndroidDevice object.
    648     Returns: Assert if turning off/on WiFi fails.
    649 
    650     """
    651     ad.log.debug("Toggling wifi OFF.")
    652     wifi_toggle_state(ad, False)
    653     time.sleep(DEFAULT_TIMEOUT)
    654     ad.log.debug("Toggling wifi ON.")
    655     wifi_toggle_state(ad, True)
    656     time.sleep(DEFAULT_TIMEOUT)
    657 
    658 
    659 def wifi_forget_network(ad, net_ssid):
    660     """Remove configured Wifi network on an android device.
    661 
    662     Args:
    663         ad: android_device object for forget network.
    664         net_ssid: ssid of network to be forget
    665 
    666     """
    667     ad.droid.wifiToggleState(True)
    668     networks = ad.droid.wifiGetConfiguredNetworks()
    669     if not networks:
    670         return
    671     for n in networks:
    672         if net_ssid in n[WifiEnums.SSID_KEY]:
    673             ad.droid.wifiForgetNetwork(n['networkId'])
    674             try:
    675                 event = ad.ed.pop_event(wifi_constants.WIFI_FORGET_NW_SUCCESS,
    676                                         SHORT_TIMEOUT)
    677             except Empty:
    678                 asserts.fail("Failed to remove network %s." % n)
    679 
    680 
    681 def wifi_test_device_init(ad):
    682     """Initializes an android device for wifi testing.
    683 
    684     0. Make sure SL4A connection is established on the android device.
    685     1. Disable location service's WiFi scan.
    686     2. Turn WiFi on.
    687     3. Clear all saved networks.
    688     4. Set country code to US.
    689     5. Enable WiFi verbose logging.
    690     6. Sync device time with computer time.
    691     7. Turn off cellular data.
    692     8. Turn off ambient display.
    693     """
    694     utils.require_sl4a((ad, ))
    695     ad.droid.wifiScannerToggleAlwaysAvailable(False)
    696     msg = "Failed to turn off location service's scan."
    697     asserts.assert_true(not ad.droid.wifiScannerIsAlwaysAvailable(), msg)
    698     wifi_toggle_state(ad, True)
    699     reset_wifi(ad)
    700     ad.droid.wifiEnableVerboseLogging(1)
    701     msg = "Failed to enable WiFi verbose logging."
    702     asserts.assert_equal(ad.droid.wifiGetVerboseLoggingLevel(), 1, msg)
    703     # We don't verify the following settings since they are not critical.
    704     # Set wpa_supplicant log level to EXCESSIVE.
    705     output = ad.adb.shell("wpa_cli -i wlan0 -p -g@android:wpa_wlan0 IFNAME="
    706                           "wlan0 log_level EXCESSIVE")
    707     ad.log.info("wpa_supplicant log change status: %s", output)
    708     utils.sync_device_time(ad)
    709     ad.droid.telephonyToggleDataConnection(False)
    710     # TODO(angli): need to verify the country code was actually set. No generic
    711     # way to check right now.
    712     ad.adb.shell("halutil -country %s" % WifiEnums.CountryCode.US)
    713     utils.set_ambient_display(ad, False)
    714 
    715 
    716 def start_wifi_connection_scan(ad):
    717     """Starts a wifi connection scan and wait for results to become available.
    718 
    719     Args:
    720         ad: An AndroidDevice object.
    721     """
    722     ad.droid.wifiStartScan()
    723     try:
    724         ad.ed.pop_event("WifiManagerScanResultsAvailable", 60)
    725     except Empty:
    726         asserts.fail("Wi-Fi results did not become available within 60s.")
    727 
    728 
    729 def start_wifi_background_scan(ad, scan_setting):
    730     """Starts wifi background scan.
    731 
    732     Args:
    733         ad: android_device object to initiate connection on.
    734         scan_setting: A dict representing the settings of the scan.
    735 
    736     Returns:
    737         If scan was started successfully, event data of success event is returned.
    738     """
    739     idx = ad.droid.wifiScannerStartBackgroundScan(scan_setting)
    740     event = ad.ed.pop_event("WifiScannerScan{}onSuccess".format(idx),
    741                             SHORT_TIMEOUT)
    742     return event['data']
    743 
    744 
    745 def start_wifi_tethering(ad, ssid, password, band=None):
    746     """Starts wifi tethering on an android_device.
    747 
    748     Args:
    749         ad: android_device to start wifi tethering on.
    750         ssid: The SSID the soft AP should broadcast.
    751         password: The password the soft AP should use.
    752         band: The band the soft AP should be set on. It should be either
    753             WifiEnums.WIFI_CONFIG_APBAND_2G or WifiEnums.WIFI_CONFIG_APBAND_5G.
    754 
    755     Returns:
    756         No return value. Error checks in this function will raise test failure signals
    757     """
    758     config = {WifiEnums.SSID_KEY: ssid}
    759     if password:
    760         config[WifiEnums.PWD_KEY] = password
    761     if band:
    762         config[WifiEnums.APBAND_KEY] = band
    763     asserts.assert_true(
    764         ad.droid.wifiSetWifiApConfiguration(config),
    765         "Failed to update WifiAp Configuration")
    766     ad.droid.wifiStartTrackingTetherStateChange()
    767     ad.droid.connectivityStartTethering(tel_defines.TETHERING_WIFI, False)
    768     try:
    769         ad.ed.pop_event("ConnectivityManagerOnTetheringStarted")
    770         ad.ed.wait_for_event("TetherStateChanged",
    771                              lambda x: x["data"]["ACTIVE_TETHER"], 30)
    772         ad.log.debug("Tethering started successfully.")
    773     except Empty:
    774         msg = "Failed to receive confirmation of wifi tethering starting"
    775         asserts.fail(msg)
    776     finally:
    777         ad.droid.wifiStopTrackingTetherStateChange()
    778 
    779 
    780 def stop_wifi_tethering(ad):
    781     """Stops wifi tethering on an android_device.
    782 
    783     Args:
    784         ad: android_device to stop wifi tethering on.
    785     """
    786     ad.droid.wifiStartTrackingTetherStateChange()
    787     ad.droid.connectivityStopTethering(tel_defines.TETHERING_WIFI)
    788     ad.droid.wifiSetApEnabled(False, None)
    789     try:
    790         ad.ed.pop_event("WifiManagerApDisabled", 30)
    791         ad.ed.wait_for_event("TetherStateChanged",
    792                              lambda x: not x["data"]["ACTIVE_TETHER"], 30)
    793     except Empty:
    794         msg = "Failed to receive confirmation of wifi tethering stopping"
    795         asserts.fail(msg)
    796     finally:
    797         ad.droid.wifiStopTrackingTetherStateChange()
    798 
    799 
    800 def toggle_wifi_and_wait_for_reconnection(ad,
    801                                           network,
    802                                           num_of_tries=1,
    803                                           assert_on_fail=True):
    804     """Toggle wifi state and then wait for Android device to reconnect to
    805     the provided wifi network.
    806 
    807     This expects the device to be already connected to the provided network.
    808 
    809     Logic steps are
    810      1. Ensure that we're connected to the network.
    811      2. Turn wifi off.
    812      3. Wait for 10 seconds.
    813      4. Turn wifi on.
    814      5. Wait for the "connected" event, then confirm the connected ssid is the
    815         one requested.
    816 
    817     Args:
    818         ad: android_device object to initiate connection on.
    819         network: A dictionary representing the network to await connection. The
    820                  dictionary must have the key "SSID".
    821         num_of_tries: An integer that is the number of times to try before
    822                       delaring failure. Default is 1.
    823         assert_on_fail: If True, error checks in this function will raise test
    824                         failure signals.
    825 
    826     Returns:
    827         If assert_on_fail is False, function returns True if the toggle was
    828         successful, False otherwise. If assert_on_fail is True, no return value.
    829     """
    830     return _assert_on_fail_handler(
    831         _toggle_wifi_and_wait_for_reconnection,
    832         assert_on_fail,
    833         ad,
    834         network,
    835         num_of_tries=num_of_tries)
    836 
    837 
    838 def _toggle_wifi_and_wait_for_reconnection(ad, network, num_of_tries=1):
    839     """Toggle wifi state and then wait for Android device to reconnect to
    840     the provided wifi network.
    841 
    842     This expects the device to be already connected to the provided network.
    843 
    844     Logic steps are
    845      1. Ensure that we're connected to the network.
    846      2. Turn wifi off.
    847      3. Wait for 10 seconds.
    848      4. Turn wifi on.
    849      5. Wait for the "connected" event, then confirm the connected ssid is the
    850         one requested.
    851 
    852     This will directly fail a test if anything goes wrong.
    853 
    854     Args:
    855         ad: android_device object to initiate connection on.
    856         network: A dictionary representing the network to await connection. The
    857                  dictionary must have the key "SSID".
    858         num_of_tries: An integer that is the number of times to try before
    859                       delaring failure. Default is 1.
    860     """
    861     expected_ssid = network[WifiEnums.SSID_KEY]
    862     # First ensure that we're already connected to the provided network.
    863     verify_con = {WifiEnums.SSID_KEY: expected_ssid}
    864     verify_wifi_connection_info(ad, verify_con)
    865     # Now toggle wifi state and wait for the connection event.
    866     wifi_toggle_state(ad, False)
    867     time.sleep(10)
    868     wifi_toggle_state(ad, True)
    869     ad.droid.wifiStartTrackingStateChange()
    870     try:
    871         connect_result = None
    872         for i in range(num_of_tries):
    873             try:
    874                 connect_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED,
    875                                                  30)
    876                 break
    877             except Empty:
    878                 pass
    879         asserts.assert_true(connect_result,
    880                             "Failed to connect to Wi-Fi network %s on %s" %
    881                             (network, ad.serial))
    882         logging.debug("Connection result on %s: %s.", ad.serial,
    883                       connect_result)
    884         actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
    885         asserts.assert_equal(actual_ssid, expected_ssid,
    886                              "Connected to the wrong network on %s."
    887                              "Expected %s, but got %s." %
    888                              (ad.serial, expected_ssid, actual_ssid))
    889         logging.info("Connected to Wi-Fi network %s on %s", actual_ssid,
    890                      ad.serial)
    891     finally:
    892         ad.droid.wifiStopTrackingStateChange()
    893 
    894 
    895 def wait_for_connect(ad, ssid=None, id=None, tries=1):
    896     """Wait for a connect event on queue and pop when available.
    897 
    898     Args:
    899         ad: An Android device object.
    900         ssid: SSID of the network to connect to.
    901         id: Network Id of the network to connect to.
    902         tries: An integer that is the number of times to try before failing.
    903 
    904     Returns:
    905         A dict with details of the connection data, which looks like this:
    906         {
    907          'time': 1485460337798,
    908          'name': 'WifiNetworkConnected',
    909          'data': {
    910                   'rssi': -27,
    911                   'is_24ghz': True,
    912                   'mac_address': '02:00:00:00:00:00',
    913                   'network_id': 1,
    914                   'BSSID': '30:b5:c2:33:d3:fc',
    915                   'ip_address': 117483712,
    916                   'link_speed': 54,
    917                   'supplicant_state': 'completed',
    918                   'hidden_ssid': False,
    919                   'SSID': 'wh_ap1_2g',
    920                   'is_5ghz': False}
    921         }
    922 
    923     """
    924     conn_result = None
    925 
    926     # If ssid and network id is None, just wait for any connect event.
    927     if id is None and ssid is None:
    928         for i in range(tries):
    929             try:
    930                 conn_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED, 30)
    931                 break
    932             except Empty:
    933                 pass
    934     else:
    935     # If ssid or network id is specified, wait for specific connect event.
    936         for i in range(tries):
    937             try:
    938                 conn_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED, 30)
    939                 if id and conn_result['data'][WifiEnums.NETID_KEY] == id:
    940                     break
    941                 elif ssid and conn_result['data'][WifiEnums.SSID_KEY] == ssid:
    942                     break
    943             except Empty:
    944                 pass
    945 
    946     return conn_result
    947 
    948 
    949 def wait_for_disconnect(ad):
    950     """Wait for a Disconnect event from the supplicant.
    951 
    952     Args:
    953         ad: Android device object.
    954 
    955     """
    956     try:
    957         ad.droid.wifiStartTrackingStateChange()
    958         event = ad.ed.pop_event("WifiNetworkDisconnected", 10)
    959         ad.droid.wifiStopTrackingStateChange()
    960     except queue.Empty:
    961         raise signals.TestFailure("Device did not disconnect from the network")
    962 
    963 
    964 def wifi_connect(ad, network, num_of_tries=1, assert_on_fail=True):
    965     """Connect an Android device to a wifi network.
    966 
    967     Initiate connection to a wifi network, wait for the "connected" event, then
    968     confirm the connected ssid is the one requested.
    969 
    970     This will directly fail a test if anything goes wrong.
    971 
    972     Args:
    973         ad: android_device object to initiate connection on.
    974         network: A dictionary representing the network to connect to. The
    975                  dictionary must have the key "SSID".
    976         num_of_tries: An integer that is the number of times to try before
    977                       delaring failure. Default is 1.
    978         assert_on_fail: If True, error checks in this function will raise test
    979                         failure signals.
    980 
    981     Returns:
    982         Returns a value only if assert_on_fail is false.
    983         Returns True if the connection was successful, False otherwise.
    984     """
    985     return _assert_on_fail_handler(
    986         _wifi_connect, assert_on_fail, ad, network, num_of_tries=num_of_tries)
    987 
    988 
    989 def _wifi_connect(ad, network, num_of_tries=1):
    990     """Connect an Android device to a wifi network.
    991 
    992     Initiate connection to a wifi network, wait for the "connected" event, then
    993     confirm the connected ssid is the one requested.
    994 
    995     This will directly fail a test if anything goes wrong.
    996 
    997     Args:
    998         ad: android_device object to initiate connection on.
    999         network: A dictionary representing the network to connect to. The
   1000                  dictionary must have the key "SSID".
   1001         num_of_tries: An integer that is the number of times to try before
   1002                       delaring failure. Default is 1.
   1003     """
   1004     asserts.assert_true(WifiEnums.SSID_KEY in network,
   1005                         "Key '%s' must be present in network definition." %
   1006                         WifiEnums.SSID_KEY)
   1007     ad.droid.wifiStartTrackingStateChange()
   1008     expected_ssid = network[WifiEnums.SSID_KEY]
   1009     ad.droid.wifiConnectByConfig(network)
   1010     ad.log.info("Starting connection process to %s", expected_ssid)
   1011     try:
   1012         event = ad.ed.pop_event(wifi_constants.CONNECT_BY_CONFIG_SUCCESS, 30)
   1013         connect_result = wait_for_connect(ad, ssid=expected_ssid, tries=num_of_tries)
   1014         asserts.assert_true(connect_result,
   1015                             "Failed to connect to Wi-Fi network %s on %s" %
   1016                             (network, ad.serial))
   1017         ad.log.debug("Wi-Fi connection result: %s.", connect_result)
   1018         actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
   1019         asserts.assert_equal(actual_ssid, expected_ssid,
   1020                              "Connected to the wrong network on %s." %
   1021                              ad.serial)
   1022         ad.log.info("Connected to Wi-Fi network %s.", actual_ssid)
   1023 
   1024         # Wait for data connection to stabilize.
   1025         time.sleep(5)
   1026 
   1027         internet = validate_connection(ad, DEFAULT_PING_ADDR)
   1028         if not internet:
   1029             raise signals.TestFailure("Failed to connect to internet on %s" %
   1030                                       expected_ssid)
   1031     except Empty:
   1032         asserts.fail("Failed to start connection process to %s on %s" %
   1033                      (network, ad.serial))
   1034     except Exception as error:
   1035         ad.log.error("Failed to connect to %s with error %s", expected_ssid,
   1036                      error)
   1037         raise signals.TestFailure("Failed to connect to %s network" % network)
   1038 
   1039     finally:
   1040         ad.droid.wifiStopTrackingStateChange()
   1041 
   1042 
   1043 def wifi_connect_by_id(ad, network_id, num_of_tries=3, assert_on_fail=True):
   1044     """Connect an Android device to a wifi network using network Id.
   1045 
   1046     Start connection to the wifi network, with the given network Id, wait for
   1047     the "connected" event, then verify the connected network is the one requested.
   1048 
   1049     This will directly fail a test if anything goes wrong.
   1050 
   1051     Args:
   1052         ad: android_device object to initiate connection on.
   1053         network_id: Integer specifying the network id of the network.
   1054         num_of_tries: An integer that is the number of times to try before
   1055                       delaring failure. Default is 1.
   1056         assert_on_fail: If True, error checks in this function will raise test
   1057                         failure signals.
   1058 
   1059     Returns:
   1060         Returns a value only if assert_on_fail is false.
   1061         Returns True if the connection was successful, False otherwise.
   1062     """
   1063     _assert_on_fail_handler(_wifi_connect_by_id, assert_on_fail, ad,
   1064                             network_id, num_of_tries)
   1065 
   1066 
   1067 def _wifi_connect_by_id(ad, network_id, num_of_tries=1):
   1068     """Connect an Android device to a wifi network using it's network id.
   1069 
   1070     Start connection to the wifi network, with the given network id, wait for
   1071     the "connected" event, then verify the connected network is the one requested.
   1072 
   1073     Args:
   1074         ad: android_device object to initiate connection on.
   1075         network_id: Integer specifying the network id of the network.
   1076         num_of_tries: An integer that is the number of times to try before
   1077                       delaring failure. Default is 1.
   1078     """
   1079     ad.droid.wifiStartTrackingStateChange()
   1080     # Clear all previous events.
   1081     ad.ed.clear_all_events()
   1082     ad.droid.wifiConnectByNetworkId(network_id)
   1083     ad.log.info("Starting connection to network with id %d", network_id)
   1084     try:
   1085         event = ad.ed.pop_event(wifi_constants.CONNECT_BY_NETID_SUCCESS, 60)
   1086         connect_result = wait_for_connect(ad, id=network_id, tries=num_of_tries)
   1087         asserts.assert_true(connect_result,
   1088                             "Failed to connect to Wi-Fi network using network id")
   1089         ad.log.debug("Wi-Fi connection result: %s", connect_result)
   1090         actual_id = connect_result['data'][WifiEnums.NETID_KEY]
   1091         asserts.assert_equal(actual_id, network_id,
   1092                              "Connected to the wrong network on %s."
   1093                              "Expected network id = %d, but got %d." %
   1094                              (ad.serial, network_id, actual_id))
   1095         expected_ssid = connect_result['data'][WifiEnums.SSID_KEY]
   1096         ad.log.info("Connected to Wi-Fi network %s with %d network id.",
   1097                      expected_ssid, network_id)
   1098 
   1099         # Wait for data connection to stabilize.
   1100         time.sleep(5)
   1101 
   1102         internet = validate_connection(ad, DEFAULT_PING_ADDR)
   1103         if not internet:
   1104             raise signals.TestFailure("Failed to connect to internet on %s" %
   1105                                       expected_ssid)
   1106     except Empty:
   1107         asserts.fail("Failed to connect to network with id %d on %s" %
   1108                     (network_id, ad.serial))
   1109     except Exception as error:
   1110         ad.log.error("Failed to connect to network with id %d with error %s",
   1111                       network_id, error)
   1112         raise signals.TestFailure("Failed to connect to network with network"
   1113                                   " id %d" % network_id)
   1114     finally:
   1115         ad.droid.wifiStopTrackingStateChange()
   1116 
   1117 
   1118 def wifi_passpoint_connect(ad, passpoint_network, num_of_tries=1,
   1119                            assert_on_fail=True):
   1120     """Connect an Android device to a wifi network.
   1121 
   1122     Initiate connection to a wifi network, wait for the "connected" event, then
   1123     confirm the connected ssid is the one requested.
   1124 
   1125     This will directly fail a test if anything goes wrong.
   1126 
   1127     Args:
   1128         ad: android_device object to initiate connection on.
   1129         passpoint_network: SSID of the Passpoint network to connect to.
   1130         num_of_tries: An integer that is the number of times to try before
   1131                       delaring failure. Default is 1.
   1132         assert_on_fail: If True, error checks in this function will raise test
   1133                         failure signals.
   1134 
   1135     Returns:
   1136         If assert_on_fail is False, function returns network id, if the connect was
   1137         successful, False otherwise. If assert_on_fail is True, no return value.
   1138     """
   1139     _assert_on_fail_handler(_wifi_passpoint_connect, assert_on_fail, ad,
   1140                             passpoint_network, num_of_tries = num_of_tries)
   1141 
   1142 
   1143 def _wifi_passpoint_connect(ad, passpoint_network, num_of_tries=1):
   1144     """Connect an Android device to a wifi network.
   1145 
   1146     Initiate connection to a wifi network, wait for the "connected" event, then
   1147     confirm the connected ssid is the one requested.
   1148 
   1149     This will directly fail a test if anything goes wrong.
   1150 
   1151     Args:
   1152         ad: android_device object to initiate connection on.
   1153         passpoint_network: SSID of the Passpoint network to connect to.
   1154         num_of_tries: An integer that is the number of times to try before
   1155                       delaring failure. Default is 1.
   1156     """
   1157     ad.droid.wifiStartTrackingStateChange()
   1158     expected_ssid = passpoint_network
   1159     ad.log.info("Starting connection process to passpoint %s", expected_ssid)
   1160 
   1161     try:
   1162         connect_result = wait_for_connect(ad, expected_ssid, num_of_tries)
   1163         asserts.assert_true(connect_result,
   1164                             "Failed to connect to WiFi passpoint network %s on"
   1165                             " %s" % (expected_ssid, ad.serial))
   1166         ad.log.info("Wi-Fi connection result: %s.", connect_result)
   1167         actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
   1168         asserts.assert_equal(actual_ssid, expected_ssid,
   1169                              "Connected to the wrong network on %s." % ad.serial)
   1170         ad.log.info("Connected to Wi-Fi passpoint network %s.", actual_ssid)
   1171 
   1172         # Wait for data connection to stabilize.
   1173         time.sleep(5)
   1174 
   1175         internet = validate_connection(ad, DEFAULT_PING_ADDR)
   1176         if not internet:
   1177             raise signals.TestFailure("Failed to connect to internet on %s" %
   1178                                       expected_ssid)
   1179     except Exception as error:
   1180         ad.log.error("Failed to connect to passpoint network %s with error %s",
   1181                       expected_ssid, error)
   1182         raise signals.TestFailure("Failed to connect to %s passpoint network" %
   1183                                    expected_ssid)
   1184 
   1185     finally:
   1186         ad.droid.wifiStopTrackingStateChange()
   1187 
   1188 
   1189 def delete_passpoint(ad, fqdn):
   1190     """Delete a required Passpoint configuration."""
   1191     try:
   1192         ad.droid.removePasspointConfig(fqdn)
   1193         return True
   1194     except Exception as error:
   1195         ad.log.error("Failed to remove passpoint configuration with FQDN=%s "
   1196                      "and error=%s" , fqdn, error)
   1197         return False
   1198 
   1199 
   1200 def start_wifi_single_scan(ad, scan_setting):
   1201     """Starts wifi single shot scan.
   1202 
   1203     Args:
   1204         ad: android_device object to initiate connection on.
   1205         scan_setting: A dict representing the settings of the scan.
   1206 
   1207     Returns:
   1208         If scan was started successfully, event data of success event is returned.
   1209     """
   1210     idx = ad.droid.wifiScannerStartScan(scan_setting)
   1211     event = ad.ed.pop_event("WifiScannerScan%sonSuccess" % idx, SHORT_TIMEOUT)
   1212     ad.log.debug("Got event %s", event)
   1213     return event['data']
   1214 
   1215 
   1216 def track_connection(ad, network_ssid, check_connection_count):
   1217     """Track wifi connection to network changes for given number of counts
   1218 
   1219     Args:
   1220         ad: android_device object for forget network.
   1221         network_ssid: network ssid to which connection would be tracked
   1222         check_connection_count: Integer for maximum number network connection
   1223                                 check.
   1224     Returns:
   1225         True if connection to given network happen, else return False.
   1226     """
   1227     ad.droid.wifiStartTrackingStateChange()
   1228     while check_connection_count > 0:
   1229         connect_network = ad.ed.pop_event("WifiNetworkConnected", 120)
   1230         ad.log.info("Connected to network %s", connect_network)
   1231         if (WifiEnums.SSID_KEY in connect_network['data'] and
   1232                 connect_network['data'][WifiEnums.SSID_KEY] == network_ssid):
   1233             return True
   1234         check_connection_count -= 1
   1235     ad.droid.wifiStopTrackingStateChange()
   1236     return False
   1237 
   1238 
   1239 def get_scan_time_and_channels(wifi_chs, scan_setting, stime_channel):
   1240     """Calculate the scan time required based on the band or channels in scan
   1241     setting
   1242 
   1243     Args:
   1244         wifi_chs: Object of channels supported
   1245         scan_setting: scan setting used for start scan
   1246         stime_channel: scan time per channel
   1247 
   1248     Returns:
   1249         scan_time: time required for completing a scan
   1250         scan_channels: channel used for scanning
   1251     """
   1252     scan_time = 0
   1253     scan_channels = []
   1254     if "band" in scan_setting and "channels" not in scan_setting:
   1255         scan_channels = wifi_chs.band_to_freq(scan_setting["band"])
   1256     elif "channels" in scan_setting and "band" not in scan_setting:
   1257         scan_channels = scan_setting["channels"]
   1258     scan_time = len(scan_channels) * stime_channel
   1259     for channel in scan_channels:
   1260         if channel in WifiEnums.DFS_5G_FREQUENCIES:
   1261             scan_time += 132  #passive scan time on DFS
   1262     return scan_time, scan_channels
   1263 
   1264 
   1265 def start_wifi_track_bssid(ad, track_setting):
   1266     """Start tracking Bssid for the given settings.
   1267 
   1268     Args:
   1269       ad: android_device object.
   1270       track_setting: Setting for which the bssid tracking should be started
   1271 
   1272     Returns:
   1273       If tracking started successfully, event data of success event is returned.
   1274     """
   1275     idx = ad.droid.wifiScannerStartTrackingBssids(
   1276         track_setting["bssidInfos"], track_setting["apLostThreshold"])
   1277     event = ad.ed.pop_event("WifiScannerBssid{}onSuccess".format(idx),
   1278                             SHORT_TIMEOUT)
   1279     return event['data']
   1280 
   1281 
   1282 def convert_pem_key_to_pkcs8(in_file, out_file):
   1283     """Converts the key file generated by us to the format required by
   1284     Android using openssl.
   1285 
   1286     The input file must have the extension "pem". The output file must
   1287     have the extension "der".
   1288 
   1289     Args:
   1290         in_file: The original key file.
   1291         out_file: The full path to the converted key file, including
   1292         filename.
   1293     """
   1294     asserts.assert_true(in_file.endswith(".pem"), "Input file has to be .pem.")
   1295     asserts.assert_true(
   1296         out_file.endswith(".der"), "Output file has to be .der.")
   1297     cmd = ("openssl pkcs8 -inform PEM -in {} -outform DER -out {} -nocrypt"
   1298            " -topk8").format(in_file, out_file)
   1299     utils.exe_cmd(cmd)
   1300 
   1301 
   1302 def validate_connection(ad, ping_addr):
   1303     """Validate internet connection by pinging the address provided.
   1304 
   1305     Args:
   1306         ad: android_device object.
   1307         ping_addr: address on internet for pinging.
   1308 
   1309     Returns:
   1310         ping output if successful, NULL otherwise.
   1311     """
   1312     ping = ad.droid.httpPing(ping_addr)
   1313     ad.log.info("Http ping result: %s.", ping)
   1314     return ping
   1315 
   1316 
   1317 #TODO(angli): This can only verify if an actual value is exactly the same.
   1318 # Would be nice to be able to verify an actual value is one of serveral.
   1319 def verify_wifi_connection_info(ad, expected_con):
   1320     """Verifies that the information of the currently connected wifi network is
   1321     as expected.
   1322 
   1323     Args:
   1324         expected_con: A dict representing expected key-value pairs for wifi
   1325             connection. e.g. {"SSID": "test_wifi"}
   1326     """
   1327     current_con = ad.droid.wifiGetConnectionInfo()
   1328     case_insensitive = ["BSSID", "supplicant_state"]
   1329     ad.log.debug("Current connection: %s", current_con)
   1330     for k, expected_v in expected_con.items():
   1331         # Do not verify authentication related fields.
   1332         if k == "password":
   1333             continue
   1334         msg = "Field %s does not exist in wifi connection info %s." % (
   1335             k, current_con)
   1336         if k not in current_con:
   1337             raise signals.TestFailure(msg)
   1338         actual_v = current_con[k]
   1339         if k in case_insensitive:
   1340             actual_v = actual_v.lower()
   1341             expected_v = expected_v.lower()
   1342         msg = "Expected %s to be %s, actual %s is %s." % (k, expected_v, k,
   1343                                                           actual_v)
   1344         if actual_v != expected_v:
   1345             raise signals.TestFailure(msg)
   1346 
   1347 
   1348 def expand_enterprise_config_by_phase2(config):
   1349     """Take an enterprise config and generate a list of configs, each with
   1350     a different phase2 auth type.
   1351 
   1352     Args:
   1353         config: A dict representing enterprise config.
   1354 
   1355     Returns
   1356         A list of enterprise configs.
   1357     """
   1358     results = []
   1359     phase2_types = WifiEnums.EapPhase2
   1360     if config[WifiEnums.Enterprise.EAP] == WifiEnums.Eap.PEAP:
   1361         # Skip unsupported phase2 types for PEAP.
   1362         phase2_types = [WifiEnums.EapPhase2.GTC, WifiEnums.EapPhase2.MSCHAPV2]
   1363     for phase2_type in phase2_types:
   1364         # Skip a special case for passpoint TTLS.
   1365         if (WifiEnums.Enterprise.FQDN in config and
   1366                 phase2_type == WifiEnums.EapPhase2.GTC):
   1367             continue
   1368         c = dict(config)
   1369         c[WifiEnums.Enterprise.PHASE2] = phase2_type.value
   1370         results.append(c)
   1371     return results
   1372 
   1373 
   1374 def generate_eap_test_name(config, ad=None):
   1375     """ Generates a test case name based on an EAP configuration.
   1376 
   1377     Args:
   1378         config: A dict representing an EAP credential.
   1379         ad object: Redundant but required as the same param is passed
   1380                    to test_func in run_generated_tests
   1381 
   1382     Returns:
   1383         A string representing the name of a generated EAP test case.
   1384     """
   1385     eap = WifiEnums.Eap
   1386     eap_phase2 = WifiEnums.EapPhase2
   1387     Ent = WifiEnums.Enterprise
   1388     name = "test_connect-"
   1389     eap_name = ""
   1390     for e in eap:
   1391         if e.value == config[Ent.EAP]:
   1392             eap_name = e.name
   1393             break
   1394     if "peap0" in config[WifiEnums.SSID_KEY].lower():
   1395         eap_name = "PEAP0"
   1396     if "peap1" in config[WifiEnums.SSID_KEY].lower():
   1397         eap_name = "PEAP1"
   1398     name += eap_name
   1399     if Ent.PHASE2 in config:
   1400         for e in eap_phase2:
   1401             if e.value == config[Ent.PHASE2]:
   1402                 name += "-{}".format(e.name)
   1403                 break
   1404     return name
   1405 
   1406 
   1407 def group_attenuators(attenuators):
   1408     """Groups a list of attenuators into attenuator groups for backward
   1409     compatibility reasons.
   1410 
   1411     Most legacy Wi-Fi setups have two attenuators each connected to a separate
   1412     AP. The new Wi-Fi setup has four attenuators, each connected to one channel
   1413     on an AP, so two of them are connected to one AP.
   1414 
   1415     To make the existing scripts work in the new setup, when the script needs
   1416     to attenuate one AP, it needs to set attenuation on both attenuators
   1417     connected to the same AP.
   1418 
   1419     This function groups attenuators properly so the scripts work in both
   1420     legacy and new Wi-Fi setups.
   1421 
   1422     Args:
   1423         attenuators: A list of attenuator objects, either two or four in length.
   1424 
   1425     Raises:
   1426         signals.TestFailure is raised if the attenuator list does not have two
   1427         or four objects.
   1428     """
   1429     attn0 = attenuator.AttenuatorGroup("AP0")
   1430     attn1 = attenuator.AttenuatorGroup("AP1")
   1431     # Legacy testbed setup has two attenuation channels.
   1432     num_of_attns = len(attenuators)
   1433     if num_of_attns == 2:
   1434         attn0.add(attenuators[0])
   1435         attn1.add(attenuators[1])
   1436     elif num_of_attns == 4:
   1437         attn0.add(attenuators[0])
   1438         attn0.add(attenuators[1])
   1439         attn1.add(attenuators[2])
   1440         attn1.add(attenuators[3])
   1441     else:
   1442         asserts.fail(("Either two or four attenuators are required for this "
   1443                       "test, but found %s") % num_of_attns)
   1444     return [attn0, attn1]
   1445