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 time
     18 import pprint
     19 
     20 from enum import IntEnum
     21 from queue import Empty
     22 
     23 from acts import asserts
     24 from acts import signals
     25 from acts.logger import LoggerProxy
     26 from acts.test_utils.tel import tel_defines
     27 from acts.utils import exe_cmd
     28 from acts.utils import require_sl4a
     29 from acts.utils import sync_device_time
     30 from acts.utils import trim_model_name
     31 
     32 log = LoggerProxy()
     33 
     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 class WifiEnums():
     52 
     53     SSID_KEY = "SSID"
     54     BSSID_KEY = "BSSID"
     55     PWD_KEY = "password"
     56     frequency_key = "frequency"
     57     APBAND_KEY = "apBand"
     58 
     59     WIFI_CONFIG_APBAND_2G = 0
     60     WIFI_CONFIG_APBAND_5G = 1
     61 
     62     WIFI_WPS_INFO_PBC     = 0;
     63     WIFI_WPS_INFO_DISPLAY = 1;
     64     WIFI_WPS_INFO_KEYPAD  = 2;
     65     WIFI_WPS_INFO_LABEL   = 3;
     66     WIFI_WPS_INFO_INVALID = 4;
     67 
     68     class CountryCode():
     69         CHINA = "CN"
     70         JAPAN = "JP"
     71         UK = "GB"
     72         US = "US"
     73         UNKNOWN = "UNKNOWN"
     74 
     75     # Start of Macros for EAP
     76     # EAP types
     77     class Eap(IntEnum):
     78         NONE = -1
     79         PEAP = 0
     80         TLS  = 1
     81         TTLS = 2
     82         PWD  = 3
     83         SIM  = 4
     84         AKA  = 5
     85         AKA_PRIME = 6
     86         UNAUTH_TLS = 7
     87 
     88     # EAP Phase2 types
     89     class EapPhase2(IntEnum):
     90         NONE        = 0
     91         PAP         = 1
     92         MSCHAP      = 2
     93         MSCHAPV2    = 3
     94         GTC         = 4
     95 
     96     class Enterprise:
     97     # Enterprise Config Macros
     98         EMPTY_VALUE      = "NULL"
     99         EAP              = "eap"
    100         PHASE2           = "phase2"
    101         IDENTITY         = "identity"
    102         ANON_IDENTITY    = "anonymous_identity"
    103         PASSWORD         = "password"
    104         SUBJECT_MATCH    = "subject_match"
    105         ALTSUBJECT_MATCH = "altsubject_match"
    106         DOM_SUFFIX_MATCH = "domain_suffix_match"
    107         CLIENT_CERT      = "client_cert"
    108         CA_CERT          = "ca_cert"
    109         ENGINE           = "engine"
    110         ENGINE_ID        = "engine_id"
    111         PRIVATE_KEY_ID   = "key_id"
    112         REALM            = "realm"
    113         PLMN             = "plmn"
    114         FQDN             = "FQDN"
    115         FRIENDLY_NAME    = "providerFriendlyName"
    116         ROAMING_IDS      = "roamingConsortiumIds"
    117     # End of Macros for EAP
    118 
    119     # Macros for wifi p2p.
    120     WIFI_P2P_SERVICE_TYPE_ALL = 0
    121     WIFI_P2P_SERVICE_TYPE_BONJOUR = 1
    122     WIFI_P2P_SERVICE_TYPE_UPNP = 2
    123     WIFI_P2P_SERVICE_TYPE_VENDOR_SPECIFIC = 255
    124 
    125     class ScanResult:
    126         CHANNEL_WIDTH_20MHZ = 0
    127         CHANNEL_WIDTH_40MHZ = 1
    128         CHANNEL_WIDTH_80MHZ = 2
    129         CHANNEL_WIDTH_160MHZ = 3
    130         CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4
    131 
    132     # Macros for wifi rtt.
    133     class RttType(IntEnum):
    134         TYPE_ONE_SIDED      = 1
    135         TYPE_TWO_SIDED      = 2
    136 
    137     class RttPeerType(IntEnum):
    138         PEER_TYPE_AP        = 1
    139         PEER_TYPE_STA       = 2 # Requires NAN.
    140         PEER_P2P_GO         = 3
    141         PEER_P2P_CLIENT     = 4
    142         PEER_NAN            = 5
    143 
    144     class RttPreamble(IntEnum):
    145         PREAMBLE_LEGACY  = 0x01
    146         PREAMBLE_HT      = 0x02
    147         PREAMBLE_VHT     = 0x04
    148 
    149     class RttBW(IntEnum):
    150         BW_5_SUPPORT   = 0x01
    151         BW_10_SUPPORT  = 0x02
    152         BW_20_SUPPORT  = 0x04
    153         BW_40_SUPPORT  = 0x08
    154         BW_80_SUPPORT  = 0x10
    155         BW_160_SUPPORT = 0x20
    156 
    157     class Rtt(IntEnum):
    158         STATUS_SUCCESS                  = 0
    159         STATUS_FAILURE                  = 1
    160         STATUS_FAIL_NO_RSP              = 2
    161         STATUS_FAIL_REJECTED            = 3
    162         STATUS_FAIL_NOT_SCHEDULED_YET   = 4
    163         STATUS_FAIL_TM_TIMEOUT          = 5
    164         STATUS_FAIL_AP_ON_DIFF_CHANNEL  = 6
    165         STATUS_FAIL_NO_CAPABILITY       = 7
    166         STATUS_ABORTED                  = 8
    167         STATUS_FAIL_INVALID_TS          = 9
    168         STATUS_FAIL_PROTOCOL            = 10
    169         STATUS_FAIL_SCHEDULE            = 11
    170         STATUS_FAIL_BUSY_TRY_LATER      = 12
    171         STATUS_INVALID_REQ              = 13
    172         STATUS_NO_WIFI                  = 14
    173         STATUS_FAIL_FTM_PARAM_OVERRIDE  = 15
    174 
    175         REASON_UNSPECIFIED              = -1
    176         REASON_NOT_AVAILABLE            = -2
    177         REASON_INVALID_LISTENER         = -3
    178         REASON_INVALID_REQUEST          = -4
    179 
    180     class RttParam:
    181         device_type = "deviceType"
    182         request_type = "requestType"
    183         BSSID = "bssid"
    184         channel_width = "channelWidth"
    185         frequency = "frequency"
    186         center_freq0 = "centerFreq0"
    187         center_freq1 = "centerFreq1"
    188         number_burst = "numberBurst"
    189         interval = "interval"
    190         num_samples_per_burst = "numSamplesPerBurst"
    191         num_retries_per_measurement_frame = "numRetriesPerMeasurementFrame"
    192         num_retries_per_FTMR = "numRetriesPerFTMR"
    193         lci_request = "LCIRequest"
    194         lcr_request = "LCRRequest"
    195         burst_timeout = "burstTimeout"
    196         preamble = "preamble"
    197         bandwidth = "bandwidth"
    198         margin = "margin"
    199 
    200     RTT_MARGIN_OF_ERROR = {
    201         RttBW.BW_80_SUPPORT: 2,
    202         RttBW.BW_40_SUPPORT: 5,
    203         RttBW.BW_20_SUPPORT: 5
    204     }
    205 
    206     # Macros as specified in the WifiScanner code.
    207     WIFI_BAND_UNSPECIFIED = 0      # not specified
    208     WIFI_BAND_24_GHZ = 1           # 2.4 GHz band
    209     WIFI_BAND_5_GHZ = 2            # 5 GHz band without DFS channels
    210     WIFI_BAND_5_GHZ_DFS_ONLY  = 4  # 5 GHz band with DFS channels
    211     WIFI_BAND_5_GHZ_WITH_DFS  = 6  # 5 GHz band with DFS channels
    212     WIFI_BAND_BOTH = 3             # both bands without DFS channels
    213     WIFI_BAND_BOTH_WITH_DFS = 7    # both bands with DFS channels
    214 
    215     REPORT_EVENT_AFTER_BUFFER_FULL = 0
    216     REPORT_EVENT_AFTER_EACH_SCAN = 1
    217     REPORT_EVENT_FULL_SCAN_RESULT = 2
    218 
    219     # US Wifi frequencies
    220     ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
    221                           2457, 2462]
    222     DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560, 5580,
    223                           5600, 5620, 5640, 5660, 5680, 5700, 5720]
    224     NONE_DFS_5G_FREQUENCIES = [5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805,
    225                                5825]
    226     ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
    227 
    228     band_to_frequencies = {
    229       WIFI_BAND_24_GHZ: ALL_2G_FREQUENCIES,
    230       WIFI_BAND_5_GHZ: NONE_DFS_5G_FREQUENCIES,
    231       WIFI_BAND_5_GHZ_DFS_ONLY: DFS_5G_FREQUENCIES,
    232       WIFI_BAND_5_GHZ_WITH_DFS: ALL_5G_FREQUENCIES,
    233       WIFI_BAND_BOTH: ALL_2G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES,
    234       WIFI_BAND_BOTH_WITH_DFS: ALL_5G_FREQUENCIES + ALL_2G_FREQUENCIES
    235     }
    236 
    237     # All Wifi frequencies to channels lookup.
    238     freq_to_channel = {
    239         2412: 1,
    240         2417: 2,
    241         2422: 3,
    242         2427: 4,
    243         2432: 5,
    244         2437: 6,
    245         2442: 7,
    246         2447: 8,
    247         2452: 9,
    248         2457: 10,
    249         2462: 11,
    250         2467: 12,
    251         2472: 13,
    252         2484: 14,
    253         4915: 183,
    254         4920: 184,
    255         4925: 185,
    256         4935: 187,
    257         4940: 188,
    258         4945: 189,
    259         4960: 192,
    260         4980: 196,
    261         5035: 7,
    262         5040: 8,
    263         5045: 9,
    264         5055: 11,
    265         5060: 12,
    266         5080: 16,
    267         5170: 34,
    268         5180: 36,
    269         5190: 38,
    270         5200: 40,
    271         5210: 42,
    272         5220: 44,
    273         5230: 46,
    274         5240: 48,
    275         5260: 52,
    276         5280: 56,
    277         5300: 60,
    278         5320: 64,
    279         5500: 100,
    280         5520: 104,
    281         5540: 108,
    282         5560: 112,
    283         5580: 116,
    284         5600: 120,
    285         5620: 124,
    286         5640: 128,
    287         5660: 132,
    288         5680: 136,
    289         5700: 140,
    290         5745: 149,
    291         5765: 153,
    292         5785: 157,
    293         5805: 161,
    294         5825: 165,
    295     }
    296 
    297     # All Wifi channels to frequencies lookup.
    298     channel_2G_to_freq = {
    299         1: 2412,
    300         2: 2417,
    301         3: 2422,
    302         4: 2427,
    303         5: 2432,
    304         6: 2437,
    305         7: 2442,
    306         8: 2447,
    307         9: 2452,
    308         10: 2457,
    309         11: 2462,
    310         12: 2467,
    311         13: 2472,
    312         14: 2484
    313     }
    314 
    315     channel_5G_to_freq = {
    316         183: 4915,
    317         184: 4920,
    318         185: 4925,
    319         187: 4935,
    320         188: 4940,
    321         189: 4945,
    322         192: 4960,
    323         196: 4980,
    324         7: 5035,
    325         8: 5040,
    326         9: 5045,
    327         11: 5055,
    328         12: 5060,
    329         16: 5080,
    330         34: 5170,
    331         36: 5180,
    332         38: 5190,
    333         40: 5200,
    334         42: 5210,
    335         44: 5220,
    336         46: 5230,
    337         48: 5240,
    338         52: 5260,
    339         56: 5280,
    340         60: 5300,
    341         64: 5320,
    342         100: 5500,
    343         104: 5520,
    344         108: 5540,
    345         112: 5560,
    346         116: 5580,
    347         120: 5600,
    348         124: 5620,
    349         128: 5640,
    350         132: 5660,
    351         136: 5680,
    352         140: 5700,
    353         149: 5745,
    354         153: 5765,
    355         157: 5785,
    356         161: 5805,
    357         165: 5825
    358     }
    359 
    360 class WifiEventNames:
    361     WIFI_CONNECTED = "WifiNetworkConnected"
    362     SUPPLICANT_CON_CHANGED = "SupplicantConnectionChanged"
    363     WIFI_FORGET_NW_SUCCESS = "WifiManagerForgetNetworkOnSuccess"
    364 
    365 class WifiTestUtilsError(Exception):
    366     pass
    367 
    368 class WifiChannelBase:
    369     ALL_2G_FREQUENCIES = []
    370     DFS_5G_FREQUENCIES = []
    371     NONE_DFS_5G_FREQUENCIES = []
    372     ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
    373     MIX_CHANNEL_SCAN = []
    374 
    375     def band_to_freq(self, band):
    376         _band_to_frequencies = {
    377             WifiEnums.WIFI_BAND_24_GHZ: self.ALL_2G_FREQUENCIES,
    378             WifiEnums.WIFI_BAND_5_GHZ: self.NONE_DFS_5G_FREQUENCIES,
    379             WifiEnums.WIFI_BAND_5_GHZ_DFS_ONLY: self.DFS_5G_FREQUENCIES,
    380             WifiEnums.WIFI_BAND_5_GHZ_WITH_DFS: self.ALL_5G_FREQUENCIES,
    381             WifiEnums.WIFI_BAND_BOTH: self.ALL_2G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES,
    382             WifiEnums.WIFI_BAND_BOTH_WITH_DFS: self.ALL_5G_FREQUENCIES + self.ALL_2G_FREQUENCIES
    383         }
    384         return _band_to_frequencies[band]
    385 
    386 class WifiChannelUS(WifiChannelBase):
    387     # US Wifi frequencies
    388     ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
    389                           2457, 2462]
    390     NONE_DFS_5G_FREQUENCIES = [5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805,
    391                                5825]
    392     MIX_CHANNEL_SCAN = [2412, 2437, 2462, 5180, 5200, 5280, 5260, 5300,5500, 5320,
    393                         5520, 5560, 5700, 5745, 5805]
    394 
    395     def __init__(self, model=None):
    396         if model and trim_model_name(model) in K_DEVICES:
    397             self.DFS_5G_FREQUENCIES = []
    398             self.ALL_5G_FREQUENCIES = self.NONE_DFS_5G_FREQUENCIES
    399             self.MIX_CHANNEL_SCAN = [2412, 2437, 2462, 5180, 5200, 5240, 5745, 5765]
    400         elif model and trim_model_name(model) in L_DEVICES:
    401             self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520,
    402                                        5540, 5560, 5580, 5660, 5680, 5700]
    403             self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
    404         elif model and trim_model_name(model) in L_TAP_DEVICES:
    405             self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520,
    406                                        5540, 5560, 5580, 5660, 5680, 5700, 5720]
    407             self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
    408         elif model and trim_model_name(model) in M_DEVICES:
    409             self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560,5580,
    410                                        5600, 5620, 5640, 5660, 5680, 5700]
    411             self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
    412         else:
    413             self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560,5580,
    414                                        5600, 5620, 5640, 5660, 5680, 5700, 5720]
    415             self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
    416 
    417 def match_networks(target_params, networks):
    418     """Finds the WiFi networks that match a given set of parameters in a list
    419     of WiFi networks.
    420 
    421     To be considered a match, a network needs to have all the target parameters
    422     and the values of those parameters need to equal to those of the target
    423     parameters.
    424 
    425     Args:
    426         target_params: The target parameters to match networks against.
    427         networks: A list of dict objects representing WiFi networks.
    428 
    429     Returns:
    430         The networks that match the target parameters.
    431     """
    432     results = []
    433     for n in networks:
    434         for k, v in target_params.items():
    435             if k not in n:
    436                 continue
    437             if n[k] != v:
    438                 continue
    439             results.append(n)
    440     return results
    441 
    442 def wifi_toggle_state(ad, new_state=None):
    443     """Toggles the state of wifi.
    444 
    445     Args:
    446         ad: An AndroidDevice object.
    447         new_state: Wifi state to set to. If None, opposite of the current state.
    448 
    449     Returns:
    450         True if the toggle was successful, False otherwise.
    451     """
    452     # Check if the new_state is already achieved, so we don't wait for the
    453     # state change event by mistake.
    454     if new_state == ad.droid.wifiCheckState():
    455         return True
    456     ad.droid.wifiStartTrackingStateChange()
    457     log.info("Setting wifi state to {}".format(new_state))
    458     ad.droid.wifiToggleState(new_state)
    459     try:
    460         event = ad.ed.pop_event(WifiEventNames.SUPPLICANT_CON_CHANGED, SHORT_TIMEOUT)
    461         return event['data']['Connected'] == new_state
    462     except Empty:
    463       # Supplicant connection event is not always reliable. We double check here
    464       # and call it a success as long as the new state equals the expected state.
    465         return new_state == ad.droid.wifiCheckState()
    466     finally:
    467         ad.droid.wifiStopTrackingStateChange()
    468 
    469 def reset_wifi(ad):
    470     """Clears all saved networks on a device.
    471 
    472     Args:
    473         ad: An AndroidDevice object.
    474 
    475     Raises:
    476         WifiTestUtilsError is raised if forget network operation failed.
    477     """
    478     ad.droid.wifiToggleState(True)
    479     networks = ad.droid.wifiGetConfiguredNetworks()
    480     if not networks:
    481         return
    482     for n in networks:
    483         ad.droid.wifiForgetNetwork(n['networkId'])
    484         try:
    485             event = ad.ed.pop_event(WifiEventNames.WIFI_FORGET_NW_SUCCESS,
    486               SHORT_TIMEOUT)
    487         except Empty:
    488             raise WifiTestUtilsError("Failed to remove network {}.".format(n))
    489 
    490 def wifi_forget_network(ad, net_ssid):
    491     """Remove configured Wifi network on an android device.
    492 
    493     Args:
    494         ad: android_device object for forget network.
    495         net_ssid: ssid of network to be forget
    496 
    497     Raises:
    498         WifiTestUtilsError is raised if forget network operation failed.
    499     """
    500     droid, ed = ad.droid, ad.ed
    501     droid.wifiToggleState(True)
    502     networks = droid.wifiGetConfiguredNetworks()
    503     if not networks:
    504         return
    505     for n in networks:
    506         if net_ssid in n[WifiEnums.SSID_KEY]:
    507             droid.wifiForgetNetwork(n['networkId'])
    508             try:
    509                 event = ed.pop_event(WifiEventNames.WIFI_FORGET_NW_SUCCESS,
    510                         SHORT_TIMEOUT)
    511             except Empty:
    512                 raise WifiTestUtilsError("Failed to remove network %s." % n)
    513 
    514 def wifi_test_device_init(ad):
    515     """Initializes an android device for wifi testing.
    516 
    517     0. Make sure SL4A connection is established on the android device.
    518     1. Disable location service's WiFi scan.
    519     2. Turn WiFi on.
    520     3. Clear all saved networks.
    521     4. Set country code to US.
    522     5. Enable WiFi verbose logging.
    523     6. Sync device time with computer time.
    524     7. Turn off cellular data.
    525     """
    526     require_sl4a((ad,))
    527     ad.droid.wifiScannerToggleAlwaysAvailable(False)
    528     msg = "Failed to turn off location service's scan."
    529     assert not ad.droid.wifiScannerIsAlwaysAvailable(), msg
    530     msg = "Failed to turn WiFi on %s" % ad.serial
    531     assert wifi_toggle_state(ad, True), msg
    532     reset_wifi(ad)
    533     msg = "Failed to clear configured networks."
    534     assert not ad.droid.wifiGetConfiguredNetworks(), msg
    535     ad.droid.wifiEnableVerboseLogging(1)
    536     msg = "Failed to enable WiFi verbose logging."
    537     assert ad.droid.wifiGetVerboseLoggingLevel() == 1, msg
    538     ad.droid.wifiScannerToggleAlwaysAvailable(False)
    539     # We don't verify the following settings since they are not critical.
    540     sync_device_time(ad)
    541     ad.droid.telephonyToggleDataConnection(False)
    542     # TODO(angli): need to verify the country code was actually set. No generic
    543     # way to check right now.
    544     ad.adb.shell("halutil -country %s" % WifiEnums.CountryCode.US)
    545 
    546 def sort_wifi_scan_results(results, key="level"):
    547     """Sort wifi scan results by key.
    548 
    549     Args:
    550         results: A list of results to sort.
    551         key: Name of the field to sort the results by.
    552 
    553     Returns:
    554         A list of results in sorted order.
    555     """
    556     return sorted(results, lambda d: (key not in d, d[key]))
    557 
    558 def start_wifi_connection_scan(ad):
    559     """Starts a wifi connection scan and wait for results to become available.
    560 
    561     Args:
    562         ad: An AndroidDevice object.
    563     """
    564     ad.droid.wifiStartScan()
    565     ad.ed.pop_event("WifiManagerScanResultsAvailable", 60)
    566 
    567 def start_wifi_background_scan(ad, scan_setting):
    568     """Starts wifi background scan.
    569 
    570     Args:
    571         ad: android_device object to initiate connection on.
    572         scan_setting: A dict representing the settings of the scan.
    573 
    574     Returns:
    575         If scan was started successfully, event data of success event is returned.
    576     """
    577     droid, ed = ad.droids[0], ad.eds[0]
    578     idx = droid.wifiScannerStartBackgroundScan(scan_setting)
    579     event = ed.pop_event("WifiScannerScan{}onSuccess".format(idx),
    580                          SHORT_TIMEOUT)
    581     return event['data']
    582 
    583 def start_wifi_tethering(ad, ssid, password, band=None):
    584     """Starts wifi tethering on an android_device.
    585 
    586     Args:
    587         ad: android_device to start wifi tethering on.
    588         ssid: The SSID the soft AP should broadcast.
    589         password: The password the soft AP should use.
    590         band: The band the soft AP should be set on. It should be either
    591             WifiEnums.WIFI_CONFIG_APBAND_2G or WifiEnums.WIFI_CONFIG_APBAND_5G.
    592 
    593     Returns:
    594         True if soft AP was started successfully, False otherwise.
    595     """
    596     droid, ed = ad.droid, ad.ed
    597     config = {
    598         WifiEnums.SSID_KEY: ssid
    599     }
    600     if password:
    601         config[WifiEnums.PWD_KEY] = password
    602     if band:
    603         config[WifiEnums.APBAND_KEY] = band
    604     if not droid.wifiSetWifiApConfiguration(config):
    605         log.error("Failed to update WifiAp Configuration")
    606         return False
    607     droid.connectivityStartTethering(tel_defines.TETHERING_WIFI, False)
    608     ed.pop_event("ConnectivityManagerOnTetheringStarted")
    609     return True
    610 
    611 def stop_wifi_tethering(ad):
    612     """Stops wifi tethering on an android_device.
    613 
    614     Args:
    615         ad: android_device to stop wifi tethering on.
    616     """
    617     droid, ed = ad.droid, ad.ed
    618     droid.wifiStartTrackingTetherStateChange()
    619     droid.connectivityStopTethering(tel_defines.TETHERING_WIFI)
    620     droid.wifiSetApEnabled(False, None)
    621     ed.pop_event("WifiManagerApDisabled", 30)
    622     ed.wait_for_event("TetherStateChanged",
    623         lambda x : not x["data"]["ACTIVE_TETHER"], 30)
    624     droid.wifiStopTrackingTetherStateChange()
    625 
    626 def wifi_connect(ad, network):
    627     """Connect an Android device to a wifi network.
    628 
    629     Initiate connection to a wifi network, wait for the "connected" event, then
    630     confirm the connected ssid is the one requested.
    631 
    632     Args:
    633         ad: android_device object to initiate connection on.
    634         network: A dictionary representing the network to connect to. The
    635             dictionary must have the key "SSID".
    636     """
    637     assert WifiEnums.SSID_KEY in network, ("Key '%s' must be present in "
    638         "network definition.") % WifiEnums.SSID_KEY
    639     ad.droid.wifiStartTrackingStateChange()
    640     try:
    641         assert ad.droid.wifiConnect(network), "WiFi connect returned false."
    642         connect_result = ad.ed.pop_event(WifiEventNames.WIFI_CONNECTED)
    643         log.debug("Connection result: %s." % connect_result)
    644         expected_ssid = network[WifiEnums.SSID_KEY]
    645         actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
    646         assert actual_ssid == expected_ssid, ("Expected to connect to %s, "
    647             "connected to %s") % (expected_ssid, actual_ssid)
    648         log.info("Successfully connected to %s" % actual_ssid)
    649     finally:
    650         ad.droid.wifiStopTrackingStateChange()
    651 
    652 def start_wifi_single_scan(ad, scan_setting):
    653     """Starts wifi single shot scan.
    654 
    655     Args:
    656         ad: android_device object to initiate connection on.
    657         scan_setting: A dict representing the settings of the scan.
    658 
    659     Returns:
    660         If scan was started successfully, event data of success event is returned.
    661     """
    662     droid, ed = ad.droid, ad.ed
    663     idx = droid.wifiScannerStartScan(scan_setting)
    664     event = ed.pop_event("WifiScannerScan{}onSuccess".format(idx),
    665                          SHORT_TIMEOUT)
    666     log.debug("event {}".format(event))
    667     return event['data']
    668 
    669 def track_connection(ad, network_ssid, check_connection_count):
    670     """Track wifi connection to network changes for given number of counts
    671 
    672     Args:
    673         ad: android_device object for forget network.
    674         network_ssid: network ssid to which connection would be tracked
    675         check_connection_count: Integer for maximum number network connection
    676             check.
    677     Returns:
    678 
    679         True if connection to given network happen, else return False.
    680     """
    681     droid, ed = ad.droid, ad.ed
    682     droid.wifiStartTrackingStateChange()
    683     while check_connection_count > 0:
    684         connect_network = ed.pop_event("WifiNetworkConnected", 120)
    685         log.info("connect_network {}".format(connect_network))
    686         if (WifiEnums.SSID_KEY in connect_network['data']
    687             and connect_network['data'][WifiEnums.SSID_KEY] == network_ssid):
    688                 return True
    689         check_connection_count -= 1
    690     droid.wifiStopTrackingStateChange()
    691     return False
    692 
    693 def get_scan_time_and_channels(wifi_chs, scan_setting, stime_channel):
    694     """Calculate the scan time required based on the band or channels in scan
    695     setting
    696 
    697     Args:
    698         wifi_chs: Object of channels supported
    699         scan_setting: scan setting used for start scan
    700         stime_channel: scan time per channel
    701 
    702     Returns:
    703         scan_time: time required for completing a scan
    704         scan_channels: channel used for scanning
    705     """
    706     scan_time = 0
    707     scan_channels = []
    708     if "band" in scan_setting and "channels" not in scan_setting:
    709         scan_channels = wifi_chs.band_to_freq(scan_setting["band"])
    710     elif "channels" in scan_setting and "band" not in scan_setting:
    711         scan_channels = scan_setting["channels"]
    712     scan_time = len(scan_channels) * stime_channel
    713     for channel in scan_channels:
    714         if channel in WifiEnums.DFS_5G_FREQUENCIES:
    715             scan_time += 132 #passive scan time on DFS
    716     return scan_time, scan_channels
    717 
    718 def start_wifi_track_bssid(ad, track_setting):
    719     """Start tracking Bssid for the given settings.
    720 
    721     Args:
    722       ad: android_device object.
    723       track_setting: Setting for which the bssid tracking should be started
    724 
    725     Returns:
    726       If tracking started successfully, event data of success event is returned.
    727     """
    728     droid, ed = ad.droid, ad.ed
    729     idx = droid.wifiScannerStartTrackingBssids(
    730         track_setting["bssidInfos"],
    731         track_setting["apLostThreshold"]
    732         )
    733     event = ed.pop_event("WifiScannerBssid{}onSuccess".format(idx),
    734                          SHORT_TIMEOUT)
    735     return event['data']
    736 
    737 def convert_pem_key_to_pkcs8(in_file, out_file):
    738     """Converts the key file generated by us to the format required by
    739     Android using openssl.
    740 
    741     The input file must have the extension "pem". The output file must
    742     have the extension "der".
    743 
    744     Args:
    745         in_file: The original key file.
    746         out_file: The full path to the converted key file, including
    747         filename.
    748     """
    749     cmd = ("openssl pkcs8 -inform PEM -in {} -outform DER -out {} -nocrypt"
    750            " -topk8").format(in_file, out_file)
    751     exe_cmd(cmd)
    752 
    753 def check_internet_connection(ad, ping_addr):
    754     """Validate internet connection by pinging the address provided.
    755 
    756     Args:
    757         ad: android_device object.
    758         ping_addr: address on internet for pinging.
    759 
    760     Returns:
    761         True, if address ping successful
    762     """
    763     droid, ed = ad.droid, ad.ed
    764     ping = droid.httpPing(ping_addr)
    765     log.info("Http ping result: {}".format(ping))
    766     return ping
    767 
    768 #TODO(angli): This can only verify if an actual value is exactly the same.
    769 # Would be nice to be able to verify an actual value is one of serveral.
    770 def verify_wifi_connection_info(ad, expected_con):
    771     """Verifies that the information of the currently connected wifi network is
    772     as expected.
    773 
    774     Args:
    775         expected_con: A dict representing expected key-value pairs for wifi
    776             connection. e.g. {"SSID": "test_wifi"}
    777     """
    778     current_con = ad.droid.wifiGetConnectionInfo()
    779     case_insensitive = ["BSSID", "supplicant_state"]
    780     log.debug("Current connection: %s" % current_con)
    781     for k, expected_v in expected_con.items():
    782         # Do not verify authentication related fields.
    783         if k == "password":
    784             continue
    785         msg = "Field %s does not exist in wifi connection info %s." % (k,
    786             current_con)
    787         if k not in current_con:
    788             raise signals.TestFailure(msg)
    789         actual_v = current_con[k]
    790         if k in case_insensitive:
    791             actual_v = actual_v.lower()
    792             expected_v = expected_v.lower()
    793         msg = "Expected %s to be %s, actual %s is %s." % (k, expected_v, k,
    794             actual_v)
    795         if actual_v != expected_v:
    796             raise signals.TestFailure(msg)
    797 
    798 def eap_connect(config, ad, validate_con=True, ping_addr=DEFAULT_PING_ADDR):
    799     """Connects to an enterprise network and verify connection.
    800 
    801     This logic expect the enterprise network to have Internet access.
    802 
    803     Args:
    804         config: A dict representing a wifi enterprise configuration.
    805         ad: The android_device to operate with.
    806         validate_con: If True, validate Internet connection after connecting to
    807             the network.
    808 
    809     Returns:
    810         True if the connection is successful and Internet access works.
    811     """
    812     droid, ed = ad.droid, ad.ed
    813     start_wifi_connection_scan(ad)
    814     expect_ssid = None
    815     if WifiEnums.SSID_KEY in config:
    816         expect_ssid = config[WifiEnums.SSID_KEY]
    817         log.info("Connecting to %s." % expect_ssid)
    818     else:
    819         log.info("Connecting.")
    820     log.debug(pprint.pformat(config, indent=4))
    821     ad.droid.wifiEnterpriseConnect(config)
    822     try:
    823         event = ed.pop_event("WifiManagerEnterpriseConnectOnSuccess", 30)
    824         log.info("Started connecting...")
    825         event = ed.pop_event(WifiEventNames.WIFI_CONNECTED, 60)
    826     except Empty:
    827         asserts.fail("Failed to connect to %s" % config)
    828     log.debug(event)
    829     if expect_ssid:
    830         actual_ssid = event["data"][WifiEnums.SSID_KEY]
    831         asserts.assert_equal(expect_ssid, actual_ssid, "SSID mismatch.")
    832     else:
    833         log.info("Connected to %s." % expect_ssid)
    834     if validate_con:
    835         log.info("Checking Internet access.")
    836         # Wait for data connection to stabilize.
    837         time.sleep(4)
    838         ping = ad.droid.httpPing(ping_addr)
    839         log.info("Http ping result: {}".format(ping))
    840         asserts.assert_true(ping, "No Internet access.")
    841 
    842 def expand_enterprise_config_by_phase2(config):
    843     """Take an enterprise config and generate a list of configs, each with
    844     a different phase2 auth type.
    845 
    846     Args:
    847         config: A dict representing enterprise config.
    848 
    849     Returns
    850         A list of enterprise configs.
    851     """
    852     results = []
    853     phase2_types = WifiEnums.EapPhase2
    854     if config[WifiEnums.Enterprise.EAP] == WifiEnums.Eap.PEAP:
    855         # Skip unsupported phase2 types for PEAP.
    856         phase2_types = [WifiEnums.EapPhase2.GTC, WifiEnums.EapPhase2.MSCHAPV2]
    857     for phase2_type in phase2_types:
    858         # Skip a special case for passpoint TTLS.
    859         if (WifiEnums.Enterprise.FQDN in config and
    860             phase2_type == WifiEnums.EapPhase2.GTC):
    861             continue
    862         c = dict(config)
    863         c[WifiEnums.Enterprise.PHASE2] = phase2_type
    864         results.append(c)
    865     return results
    866