• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
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
17import logging
18import os
19import re
20import shutil
21import time
22
23from retry import retry
24
25from collections import namedtuple
26from enum import IntEnum
27from queue import Empty
28
29from acts import asserts
30from acts import context
31from acts import signals
32from acts import utils
33from acts.controllers import attenuator
34from acts.controllers.ap_lib import hostapd_security
35from acts.controllers.ap_lib import hostapd_ap_preset
36from acts.controllers.ap_lib.hostapd_constants import BAND_2G
37from acts.controllers.ap_lib.hostapd_constants import BAND_5G
38from acts_contrib.test_utils.net import connectivity_const as cconsts
39from acts_contrib.test_utils.tel import tel_defines
40from acts_contrib.test_utils.wifi import wifi_constants
41from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
42
43# Default timeout used for reboot, toggle WiFi and Airplane mode,
44# for the system to settle down after the operation.
45DEFAULT_TIMEOUT = 10
46# Number of seconds to wait for events that are supposed to happen quickly.
47# Like onSuccess for start background scan and confirmation on wifi state
48# change.
49SHORT_TIMEOUT = 30
50ROAMING_TIMEOUT = 30
51WIFI_CONNECTION_TIMEOUT_DEFAULT = 30
52# Speed of light in m/s.
53SPEED_OF_LIGHT = 299792458
54
55DEFAULT_PING_ADDR = "https://www.google.com/robots.txt"
56
57CNSS_DIAG_CONFIG_PATH = "/data/vendor/wifi/cnss_diag/"
58CNSS_DIAG_CONFIG_FILE = "cnss_diag.conf"
59
60ROAMING_ATTN = {
61    "AP1_on_AP2_off": [0, 0, 95, 95],
62    "AP1_off_AP2_on": [95, 95, 0, 0],
63    "default": [0, 0, 0, 0]
64}
65
66
67class WifiEnums():
68
69    SSID_KEY = "SSID"  # Used for Wifi & SoftAp
70    SSID_PATTERN_KEY = "ssidPattern"
71    NETID_KEY = "network_id"
72    BSSID_KEY = "BSSID"  # Used for Wifi & SoftAp
73    BSSID_PATTERN_KEY = "bssidPattern"
74    PWD_KEY = "password"  # Used for Wifi & SoftAp
75    frequency_key = "frequency"
76    HIDDEN_KEY = "hiddenSSID"  # Used for Wifi & SoftAp
77    IS_APP_INTERACTION_REQUIRED = "isAppInteractionRequired"
78    IS_USER_INTERACTION_REQUIRED = "isUserInteractionRequired"
79    IS_SUGGESTION_METERED = "isMetered"
80    PRIORITY = "priority"
81    SECURITY = "security"  # Used for Wifi & SoftAp
82
83    # Used for SoftAp
84    AP_BAND_KEY = "apBand"
85    AP_CHANNEL_KEY = "apChannel"
86    AP_BANDS_KEY = "apBands"
87    AP_CHANNEL_FREQUENCYS_KEY = "apChannelFrequencies"
88    AP_MAC_RANDOMIZATION_SETTING_KEY = "MacRandomizationSetting"
89    AP_BRIDGED_OPPORTUNISTIC_SHUTDOWN_ENABLE_KEY = "BridgedModeOpportunisticShutdownEnabled"
90    AP_IEEE80211AX_ENABLED_KEY = "Ieee80211axEnabled"
91    AP_MAXCLIENTS_KEY = "MaxNumberOfClients"
92    AP_SHUTDOWNTIMEOUT_KEY = "ShutdownTimeoutMillis"
93    AP_SHUTDOWNTIMEOUTENABLE_KEY = "AutoShutdownEnabled"
94    AP_CLIENTCONTROL_KEY = "ClientControlByUserEnabled"
95    AP_ALLOWEDLIST_KEY = "AllowedClientList"
96    AP_BLOCKEDLIST_KEY = "BlockedClientList"
97
98    WIFI_CONFIG_SOFTAP_BAND_2G = 1
99    WIFI_CONFIG_SOFTAP_BAND_5G = 2
100    WIFI_CONFIG_SOFTAP_BAND_2G_5G = 3
101    WIFI_CONFIG_SOFTAP_BAND_6G = 4
102    WIFI_CONFIG_SOFTAP_BAND_2G_6G = 5
103    WIFI_CONFIG_SOFTAP_BAND_5G_6G = 6
104    WIFI_CONFIG_SOFTAP_BAND_ANY = 7
105
106    # DO NOT USE IT for new test case! Replaced by WIFI_CONFIG_SOFTAP_BAND_
107    WIFI_CONFIG_APBAND_2G = WIFI_CONFIG_SOFTAP_BAND_2G
108    WIFI_CONFIG_APBAND_5G = WIFI_CONFIG_SOFTAP_BAND_5G
109    WIFI_CONFIG_APBAND_AUTO = WIFI_CONFIG_SOFTAP_BAND_2G_5G
110
111    WIFI_CONFIG_APBAND_2G_OLD = 0
112    WIFI_CONFIG_APBAND_5G_OLD = 1
113    WIFI_CONFIG_APBAND_AUTO_OLD = -1
114
115    WIFI_WPS_INFO_PBC = 0
116    WIFI_WPS_INFO_DISPLAY = 1
117    WIFI_WPS_INFO_KEYPAD = 2
118    WIFI_WPS_INFO_LABEL = 3
119    WIFI_WPS_INFO_INVALID = 4
120
121    class SoftApSecurityType():
122        OPEN = "NONE"
123        WPA2 = "WPA2_PSK"
124        WPA3_SAE_TRANSITION = "WPA3_SAE_TRANSITION"
125        WPA3_SAE = "WPA3_SAE"
126
127    class CountryCode():
128        AUSTRALIA = "AU"
129        CHINA = "CN"
130        GERMANY = "DE"
131        JAPAN = "JP"
132        UK = "GB"
133        US = "US"
134        UNKNOWN = "UNKNOWN"
135
136    # Start of Macros for EAP
137    # EAP types
138    class Eap(IntEnum):
139        NONE = -1
140        PEAP = 0
141        TLS = 1
142        TTLS = 2
143        PWD = 3
144        SIM = 4
145        AKA = 5
146        AKA_PRIME = 6
147        UNAUTH_TLS = 7
148
149    # EAP Phase2 types
150    class EapPhase2(IntEnum):
151        NONE = 0
152        PAP = 1
153        MSCHAP = 2
154        MSCHAPV2 = 3
155        GTC = 4
156
157    class Enterprise:
158        # Enterprise Config Macros
159        EMPTY_VALUE = "NULL"
160        EAP = "eap"
161        PHASE2 = "phase2"
162        IDENTITY = "identity"
163        ANON_IDENTITY = "anonymous_identity"
164        PASSWORD = "password"
165        SUBJECT_MATCH = "subject_match"
166        ALTSUBJECT_MATCH = "altsubject_match"
167        DOM_SUFFIX_MATCH = "domain_suffix_match"
168        CLIENT_CERT = "client_cert"
169        CA_CERT = "ca_cert"
170        ENGINE = "engine"
171        ENGINE_ID = "engine_id"
172        PRIVATE_KEY_ID = "key_id"
173        REALM = "realm"
174        PLMN = "plmn"
175        FQDN = "FQDN"
176        FRIENDLY_NAME = "providerFriendlyName"
177        ROAMING_IDS = "roamingConsortiumIds"
178        OCSP = "ocsp"
179
180    # End of Macros for EAP
181
182    # Macros for wifi p2p.
183    WIFI_P2P_SERVICE_TYPE_ALL = 0
184    WIFI_P2P_SERVICE_TYPE_BONJOUR = 1
185    WIFI_P2P_SERVICE_TYPE_UPNP = 2
186    WIFI_P2P_SERVICE_TYPE_VENDOR_SPECIFIC = 255
187
188    class ScanResult:
189        CHANNEL_WIDTH_20MHZ = 0
190        CHANNEL_WIDTH_40MHZ = 1
191        CHANNEL_WIDTH_80MHZ = 2
192        CHANNEL_WIDTH_160MHZ = 3
193        CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4
194
195    # Macros for wifi rtt.
196    class RttType(IntEnum):
197        TYPE_ONE_SIDED = 1
198        TYPE_TWO_SIDED = 2
199
200    class RttPeerType(IntEnum):
201        PEER_TYPE_AP = 1
202        PEER_TYPE_STA = 2  # Requires NAN.
203        PEER_P2P_GO = 3
204        PEER_P2P_CLIENT = 4
205        PEER_NAN = 5
206
207    class RttPreamble(IntEnum):
208        PREAMBLE_LEGACY = 0x01
209        PREAMBLE_HT = 0x02
210        PREAMBLE_VHT = 0x04
211
212    class RttBW(IntEnum):
213        BW_5_SUPPORT = 0x01
214        BW_10_SUPPORT = 0x02
215        BW_20_SUPPORT = 0x04
216        BW_40_SUPPORT = 0x08
217        BW_80_SUPPORT = 0x10
218        BW_160_SUPPORT = 0x20
219
220    class Rtt(IntEnum):
221        STATUS_SUCCESS = 0
222        STATUS_FAILURE = 1
223        STATUS_FAIL_NO_RSP = 2
224        STATUS_FAIL_REJECTED = 3
225        STATUS_FAIL_NOT_SCHEDULED_YET = 4
226        STATUS_FAIL_TM_TIMEOUT = 5
227        STATUS_FAIL_AP_ON_DIFF_CHANNEL = 6
228        STATUS_FAIL_NO_CAPABILITY = 7
229        STATUS_ABORTED = 8
230        STATUS_FAIL_INVALID_TS = 9
231        STATUS_FAIL_PROTOCOL = 10
232        STATUS_FAIL_SCHEDULE = 11
233        STATUS_FAIL_BUSY_TRY_LATER = 12
234        STATUS_INVALID_REQ = 13
235        STATUS_NO_WIFI = 14
236        STATUS_FAIL_FTM_PARAM_OVERRIDE = 15
237
238        REASON_UNSPECIFIED = -1
239        REASON_NOT_AVAILABLE = -2
240        REASON_INVALID_LISTENER = -3
241        REASON_INVALID_REQUEST = -4
242
243    class RttParam:
244        device_type = "deviceType"
245        request_type = "requestType"
246        BSSID = "bssid"
247        channel_width = "channelWidth"
248        frequency = "frequency"
249        center_freq0 = "centerFreq0"
250        center_freq1 = "centerFreq1"
251        number_burst = "numberBurst"
252        interval = "interval"
253        num_samples_per_burst = "numSamplesPerBurst"
254        num_retries_per_measurement_frame = "numRetriesPerMeasurementFrame"
255        num_retries_per_FTMR = "numRetriesPerFTMR"
256        lci_request = "LCIRequest"
257        lcr_request = "LCRRequest"
258        burst_timeout = "burstTimeout"
259        preamble = "preamble"
260        bandwidth = "bandwidth"
261        margin = "margin"
262
263    RTT_MARGIN_OF_ERROR = {
264        RttBW.BW_80_SUPPORT: 2,
265        RttBW.BW_40_SUPPORT: 5,
266        RttBW.BW_20_SUPPORT: 5
267    }
268
269    # Macros as specified in the WifiScanner code.
270    WIFI_BAND_UNSPECIFIED = 0  # not specified
271    WIFI_BAND_24_GHZ = 1  # 2.4 GHz band
272    WIFI_BAND_5_GHZ = 2  # 5 GHz band without DFS channels
273    WIFI_BAND_5_GHZ_DFS_ONLY = 4  # 5 GHz band with DFS channels
274    WIFI_BAND_5_GHZ_WITH_DFS = 6  # 5 GHz band with DFS channels
275    WIFI_BAND_BOTH = 3  # both bands without DFS channels
276    WIFI_BAND_BOTH_WITH_DFS = 7  # both bands with DFS channels
277
278    REPORT_EVENT_AFTER_BUFFER_FULL = 0
279    REPORT_EVENT_AFTER_EACH_SCAN = 1
280    REPORT_EVENT_FULL_SCAN_RESULT = 2
281
282    SCAN_TYPE_LOW_LATENCY = 0
283    SCAN_TYPE_LOW_POWER = 1
284    SCAN_TYPE_HIGH_ACCURACY = 2
285
286    # US Wifi frequencies
287    ALL_2G_FREQUENCIES = [
288        2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462
289    ]
290    DFS_5G_FREQUENCIES = [
291        5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640,
292        5660, 5680, 5700, 5720
293    ]
294    NONE_DFS_5G_FREQUENCIES = [
295        5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805, 5825
296    ]
297    ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
298
299    band_to_frequencies = {
300        WIFI_BAND_24_GHZ: ALL_2G_FREQUENCIES,
301        WIFI_BAND_5_GHZ: NONE_DFS_5G_FREQUENCIES,
302        WIFI_BAND_5_GHZ_DFS_ONLY: DFS_5G_FREQUENCIES,
303        WIFI_BAND_5_GHZ_WITH_DFS: ALL_5G_FREQUENCIES,
304        WIFI_BAND_BOTH: ALL_2G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES,
305        WIFI_BAND_BOTH_WITH_DFS: ALL_5G_FREQUENCIES + ALL_2G_FREQUENCIES
306    }
307
308    # TODO: add all of the band mapping.
309    softap_band_frequencies = {
310        WIFI_CONFIG_SOFTAP_BAND_2G: ALL_2G_FREQUENCIES,
311        WIFI_CONFIG_SOFTAP_BAND_5G: ALL_5G_FREQUENCIES
312    }
313
314    # All Wifi frequencies to channels lookup.
315    freq_to_channel = {
316        2412: 1,
317        2417: 2,
318        2422: 3,
319        2427: 4,
320        2432: 5,
321        2437: 6,
322        2442: 7,
323        2447: 8,
324        2452: 9,
325        2457: 10,
326        2462: 11,
327        2467: 12,
328        2472: 13,
329        2484: 14,
330        4915: 183,
331        4920: 184,
332        4925: 185,
333        4935: 187,
334        4940: 188,
335        4945: 189,
336        4960: 192,
337        4980: 196,
338        5035: 7,
339        5040: 8,
340        5045: 9,
341        5055: 11,
342        5060: 12,
343        5080: 16,
344        5170: 34,
345        5180: 36,
346        5190: 38,
347        5200: 40,
348        5210: 42,
349        5220: 44,
350        5230: 46,
351        5240: 48,
352        5260: 52,
353        5280: 56,
354        5300: 60,
355        5320: 64,
356        5500: 100,
357        5520: 104,
358        5540: 108,
359        5560: 112,
360        5580: 116,
361        5600: 120,
362        5620: 124,
363        5640: 128,
364        5660: 132,
365        5680: 136,
366        5700: 140,
367        5745: 149,
368        5765: 153,
369        5785: 157,
370        5795: 159,
371        5805: 161,
372        5825: 165,
373    }
374
375    # All Wifi channels to frequencies lookup.
376    channel_2G_to_freq = {
377        1: 2412,
378        2: 2417,
379        3: 2422,
380        4: 2427,
381        5: 2432,
382        6: 2437,
383        7: 2442,
384        8: 2447,
385        9: 2452,
386        10: 2457,
387        11: 2462,
388        12: 2467,
389        13: 2472,
390        14: 2484
391    }
392
393    channel_5G_to_freq = {
394        183: 4915,
395        184: 4920,
396        185: 4925,
397        187: 4935,
398        188: 4940,
399        189: 4945,
400        192: 4960,
401        196: 4980,
402        7: 5035,
403        8: 5040,
404        9: 5045,
405        11: 5055,
406        12: 5060,
407        16: 5080,
408        34: 5170,
409        36: 5180,
410        38: 5190,
411        40: 5200,
412        42: 5210,
413        44: 5220,
414        46: 5230,
415        48: 5240,
416        50: 5250,
417        52: 5260,
418        56: 5280,
419        60: 5300,
420        64: 5320,
421        100: 5500,
422        104: 5520,
423        108: 5540,
424        112: 5560,
425        116: 5580,
426        120: 5600,
427        124: 5620,
428        128: 5640,
429        132: 5660,
430        136: 5680,
431        140: 5700,
432        149: 5745,
433        151: 5755,
434        153: 5765,
435        155: 5775,
436        157: 5785,
437        159: 5795,
438        161: 5805,
439        165: 5825
440    }
441
442
443class WifiChannelBase:
444    ALL_2G_FREQUENCIES = []
445    DFS_5G_FREQUENCIES = []
446    NONE_DFS_5G_FREQUENCIES = []
447    ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
448    MIX_CHANNEL_SCAN = []
449
450    def band_to_freq(self, band):
451        _band_to_frequencies = {
452            WifiEnums.WIFI_BAND_24_GHZ:
453            self.ALL_2G_FREQUENCIES,
454            WifiEnums.WIFI_BAND_5_GHZ:
455            self.NONE_DFS_5G_FREQUENCIES,
456            WifiEnums.WIFI_BAND_5_GHZ_DFS_ONLY:
457            self.DFS_5G_FREQUENCIES,
458            WifiEnums.WIFI_BAND_5_GHZ_WITH_DFS:
459            self.ALL_5G_FREQUENCIES,
460            WifiEnums.WIFI_BAND_BOTH:
461            self.ALL_2G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES,
462            WifiEnums.WIFI_BAND_BOTH_WITH_DFS:
463            self.ALL_5G_FREQUENCIES + self.ALL_2G_FREQUENCIES
464        }
465        return _band_to_frequencies[band]
466
467
468class WifiChannelUS(WifiChannelBase):
469    # US Wifi frequencies
470    ALL_2G_FREQUENCIES = [
471        2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462
472    ]
473    NONE_DFS_5G_FREQUENCIES = [
474        5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805, 5825
475    ]
476    MIX_CHANNEL_SCAN = [
477        2412, 2437, 2462, 5180, 5200, 5280, 5260, 5300, 5500, 5320, 5520, 5560,
478        5700, 5745, 5805
479    ]
480
481    def __init__(self, model=None, support_addition_channel=[]):
482        if model in support_addition_channel:
483            self.ALL_2G_FREQUENCIES = [
484                2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457,
485                2462, 2467, 2472
486                ]
487        self.DFS_5G_FREQUENCIES = [
488            5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560, 5580, 5600, 5620,
489            5640, 5660, 5680, 5700, 5720
490            ]
491        self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
492
493
494class WifiReferenceNetworks:
495    """ Class to parse and return networks of different band and
496        auth type from reference_networks
497    """
498    def __init__(self, obj):
499        self.reference_networks = obj
500        self.WIFI_2G = "2g"
501        self.WIFI_5G = "5g"
502
503        self.secure_networks_2g = []
504        self.secure_networks_5g = []
505        self.open_networks_2g = []
506        self.open_networks_5g = []
507        self._parse_networks()
508
509    def _parse_networks(self):
510        for network in self.reference_networks:
511            for key in network:
512                if key == self.WIFI_2G:
513                    if "password" in network[key]:
514                        self.secure_networks_2g.append(network[key])
515                    else:
516                        self.open_networks_2g.append(network[key])
517                else:
518                    if "password" in network[key]:
519                        self.secure_networks_5g.append(network[key])
520                    else:
521                        self.open_networks_5g.append(network[key])
522
523    def return_2g_secure_networks(self):
524        return self.secure_networks_2g
525
526    def return_5g_secure_networks(self):
527        return self.secure_networks_5g
528
529    def return_2g_open_networks(self):
530        return self.open_networks_2g
531
532    def return_5g_open_networks(self):
533        return self.open_networks_5g
534
535    def return_secure_networks(self):
536        return self.secure_networks_2g + self.secure_networks_5g
537
538    def return_open_networks(self):
539        return self.open_networks_2g + self.open_networks_5g
540
541
542def _assert_on_fail_handler(func, assert_on_fail, *args, **kwargs):
543    """Wrapper function that handles the bahevior of assert_on_fail.
544
545    When assert_on_fail is True, let all test signals through, which can
546    terminate test cases directly. When assert_on_fail is False, the wrapper
547    raises no test signals and reports operation status by returning True or
548    False.
549
550    Args:
551        func: The function to wrap. This function reports operation status by
552              raising test signals.
553        assert_on_fail: A boolean that specifies if the output of the wrapper
554                        is test signal based or return value based.
555        args: Positional args for func.
556        kwargs: Name args for func.
557
558    Returns:
559        If assert_on_fail is True, returns True/False to signal operation
560        status, otherwise return nothing.
561    """
562    try:
563        func(*args, **kwargs)
564        if not assert_on_fail:
565            return True
566    except signals.TestSignal:
567        if assert_on_fail:
568            raise
569        return False
570
571
572def assert_network_in_list(target, network_list):
573    """Makes sure a specified target Wi-Fi network exists in a list of Wi-Fi
574    networks.
575
576    Args:
577        target: A dict representing a Wi-Fi network.
578                E.g. {WifiEnums.SSID_KEY: "SomeNetwork"}
579        network_list: A list of dicts, each representing a Wi-Fi network.
580    """
581    match_results = match_networks(target, network_list)
582    asserts.assert_true(
583        match_results, "Target network %s, does not exist in network list %s" %
584        (target, network_list))
585
586
587def match_networks(target_params, networks):
588    """Finds the WiFi networks that match a given set of parameters in a list
589    of WiFi networks.
590
591    To be considered a match, the network should contain every key-value pair
592    of target_params
593
594    Args:
595        target_params: A dict with 1 or more key-value pairs representing a Wi-Fi network.
596                       E.g { 'SSID': 'wh_ap1_5g', 'BSSID': '30:b5:c2:33:e4:47' }
597        networks: A list of dict objects representing WiFi networks.
598
599    Returns:
600        The networks that match the target parameters.
601    """
602    results = []
603    asserts.assert_true(target_params,
604                        "Expected networks object 'target_params' is empty")
605    for n in networks:
606        add_network = 1
607        for k, v in target_params.items():
608            if k not in n:
609                add_network = 0
610                break
611            if n[k] != v:
612                add_network = 0
613                break
614        if add_network:
615            results.append(n)
616    return results
617
618
619def wait_for_wifi_state(ad, state, assert_on_fail=True):
620    """Waits for the device to transition to the specified wifi state
621
622    Args:
623        ad: An AndroidDevice object.
624        state: Wifi state to wait for.
625        assert_on_fail: If True, error checks in this function will raise test
626                        failure signals.
627
628    Returns:
629        If assert_on_fail is False, function returns True if the device transitions
630        to the specified state, False otherwise. If assert_on_fail is True, no return value.
631    """
632    return _assert_on_fail_handler(_wait_for_wifi_state,
633                                   assert_on_fail,
634                                   ad,
635                                   state=state)
636
637
638def _wait_for_wifi_state(ad, state):
639    """Toggles the state of wifi.
640
641    TestFailure signals are raised when something goes wrong.
642
643    Args:
644        ad: An AndroidDevice object.
645        state: Wifi state to wait for.
646    """
647    if state == ad.droid.wifiCheckState():
648        # Check if the state is already achieved, so we don't wait for the
649        # state change event by mistake.
650        return
651    ad.droid.wifiStartTrackingStateChange()
652    fail_msg = "Device did not transition to Wi-Fi state to %s on %s." % (
653        state, ad.serial)
654    try:
655        ad.ed.wait_for_event(wifi_constants.WIFI_STATE_CHANGED,
656                             lambda x: x["data"]["enabled"] == state,
657                             SHORT_TIMEOUT)
658    except Empty:
659        asserts.assert_equal(state, ad.droid.wifiCheckState(), fail_msg)
660    finally:
661        ad.droid.wifiStopTrackingStateChange()
662
663
664def wifi_toggle_state(ad, new_state=None, assert_on_fail=True):
665    """Toggles the state of wifi.
666
667    Args:
668        ad: An AndroidDevice object.
669        new_state: Wifi state to set to. If None, opposite of the current state.
670        assert_on_fail: If True, error checks in this function will raise test
671                        failure signals.
672
673    Returns:
674        If assert_on_fail is False, function returns True if the toggle was
675        successful, False otherwise. If assert_on_fail is True, no return value.
676    """
677    return _assert_on_fail_handler(_wifi_toggle_state,
678                                   assert_on_fail,
679                                   ad,
680                                   new_state=new_state)
681
682
683def _wifi_toggle_state(ad, new_state=None):
684    """Toggles the state of wifi.
685
686    TestFailure signals are raised when something goes wrong.
687
688    Args:
689        ad: An AndroidDevice object.
690        new_state: The state to set Wi-Fi to. If None, opposite of the current
691                   state will be set.
692    """
693    if new_state is None:
694        new_state = not ad.droid.wifiCheckState()
695    elif new_state == ad.droid.wifiCheckState():
696        # Check if the new_state is already achieved, so we don't wait for the
697        # state change event by mistake.
698        return
699    ad.droid.wifiStartTrackingStateChange()
700    ad.log.info("Setting Wi-Fi state to %s.", new_state)
701    ad.ed.clear_all_events()
702    # Setting wifi state.
703    ad.droid.wifiToggleState(new_state)
704    time.sleep(2)
705    fail_msg = "Failed to set Wi-Fi state to %s on %s." % (new_state,
706                                                           ad.serial)
707    try:
708        ad.ed.wait_for_event(wifi_constants.WIFI_STATE_CHANGED,
709                             lambda x: x["data"]["enabled"] == new_state,
710                             SHORT_TIMEOUT)
711    except Empty:
712        asserts.assert_equal(new_state, ad.droid.wifiCheckState(), fail_msg)
713    finally:
714        ad.droid.wifiStopTrackingStateChange()
715
716
717def reset_wifi(ad):
718    """Clears all saved Wi-Fi networks on a device.
719
720    This will turn Wi-Fi on.
721
722    Args:
723        ad: An AndroidDevice object.
724
725    """
726    networks = ad.droid.wifiGetConfiguredNetworks()
727    if not networks:
728        return
729    removed = []
730    for n in networks:
731        if n['networkId'] not in removed:
732            ad.droid.wifiForgetNetwork(n['networkId'])
733            removed.append(n['networkId'])
734        else:
735            continue
736        try:
737            event = ad.ed.pop_event(wifi_constants.WIFI_FORGET_NW_SUCCESS,
738                                    SHORT_TIMEOUT)
739        except Empty:
740            logging.warning("Could not confirm the removal of network %s.", n)
741    # Check again to see if there's any network left.
742    asserts.assert_true(
743        not ad.droid.wifiGetConfiguredNetworks(),
744        "Failed to remove these configured Wi-Fi networks: %s" % networks)
745
746
747def toggle_airplane_mode_on_and_off(ad):
748    """Turn ON and OFF Airplane mode.
749
750    ad: An AndroidDevice object.
751    Returns: Assert if turning on/off Airplane mode fails.
752
753    """
754    ad.log.debug("Toggling Airplane mode ON.")
755    asserts.assert_true(utils.force_airplane_mode(ad, True),
756                        "Can not turn on airplane mode on: %s" % ad.serial)
757    time.sleep(DEFAULT_TIMEOUT)
758    ad.log.debug("Toggling Airplane mode OFF.")
759    asserts.assert_true(utils.force_airplane_mode(ad, False),
760                        "Can not turn on airplane mode on: %s" % ad.serial)
761    time.sleep(DEFAULT_TIMEOUT)
762
763
764def toggle_wifi_off_and_on(ad):
765    """Turn OFF and ON WiFi.
766
767    ad: An AndroidDevice object.
768    Returns: Assert if turning off/on WiFi fails.
769
770    """
771    ad.log.debug("Toggling wifi OFF.")
772    wifi_toggle_state(ad, False)
773    time.sleep(DEFAULT_TIMEOUT)
774    ad.log.debug("Toggling wifi ON.")
775    wifi_toggle_state(ad, True)
776    time.sleep(DEFAULT_TIMEOUT)
777
778
779def wifi_forget_network(ad, net_ssid):
780    """Remove configured Wifi network on an android device.
781
782    Args:
783        ad: android_device object for forget network.
784        net_ssid: ssid of network to be forget
785
786    """
787    networks = ad.droid.wifiGetConfiguredNetworks()
788    if not networks:
789        return
790    removed = []
791    for n in networks:
792        if net_ssid in n[WifiEnums.SSID_KEY] and n['networkId'] not in removed:
793            ad.droid.wifiForgetNetwork(n['networkId'])
794            removed.append(n['networkId'])
795            try:
796                event = ad.ed.pop_event(wifi_constants.WIFI_FORGET_NW_SUCCESS,
797                                        SHORT_TIMEOUT)
798            except Empty:
799                asserts.fail("Failed to remove network %s." % n)
800            break
801
802
803def wifi_test_device_init(ad):
804    """Initializes an android device for wifi testing.
805
806    0. Make sure SL4A connection is established on the android device.
807    1. Disable location service's WiFi scan.
808    2. Turn WiFi on.
809    3. Clear all saved networks.
810    4. Set country code to US.
811    5. Enable WiFi verbose logging.
812    6. Sync device time with computer time.
813    7. Turn off cellular data.
814    8. Turn off ambient display.
815    """
816    utils.require_sl4a((ad, ))
817    ad.droid.wifiScannerToggleAlwaysAvailable(False)
818    msg = "Failed to turn off location service's scan."
819    asserts.assert_true(not ad.droid.wifiScannerIsAlwaysAvailable(), msg)
820    wifi_toggle_state(ad, True)
821    reset_wifi(ad)
822    ad.droid.wifiEnableVerboseLogging(1)
823    msg = "Failed to enable WiFi verbose logging."
824    asserts.assert_equal(ad.droid.wifiGetVerboseLoggingLevel(), 1, msg)
825    # We don't verify the following settings since they are not critical.
826    # Set wpa_supplicant log level to EXCESSIVE.
827    output = ad.adb.shell(
828        "wpa_cli -i wlan0 -p -g@android:wpa_wlan0 IFNAME="
829        "wlan0 log_level EXCESSIVE",
830        ignore_status=True)
831    ad.log.info("wpa_supplicant log change status: %s", output)
832    utils.sync_device_time(ad)
833    ad.droid.telephonyToggleDataConnection(False)
834    set_wifi_country_code(ad, WifiEnums.CountryCode.US)
835    utils.set_ambient_display(ad, False)
836
837
838def set_wifi_country_code(ad, country_code):
839    """Sets the wifi country code on the device.
840
841    Args:
842        ad: An AndroidDevice object.
843        country_code: 2 letter ISO country code
844
845    Raises:
846        An RpcException if unable to set the country code.
847    """
848    try:
849        ad.adb.shell("cmd wifi force-country-code enabled %s" % country_code)
850    except Exception as e:
851        ad.droid.wifiSetCountryCode(WifiEnums.CountryCode.US)
852
853
854def start_wifi_connection_scan(ad):
855    """Starts a wifi connection scan and wait for results to become available.
856
857    Args:
858        ad: An AndroidDevice object.
859    """
860    ad.ed.clear_all_events()
861    ad.droid.wifiStartScan()
862    try:
863        ad.ed.pop_event("WifiManagerScanResultsAvailable", 60)
864    except Empty:
865        asserts.fail("Wi-Fi results did not become available within 60s.")
866
867
868def start_wifi_connection_scan_and_return_status(ad):
869    """
870    Starts a wifi connection scan and wait for results to become available
871    or a scan failure to be reported.
872
873    Args:
874        ad: An AndroidDevice object.
875    Returns:
876        True: if scan succeeded & results are available
877        False: if scan failed
878    """
879    ad.ed.clear_all_events()
880    ad.droid.wifiStartScan()
881    try:
882        events = ad.ed.pop_events("WifiManagerScan(ResultsAvailable|Failure)",
883                                  60)
884    except Empty:
885        asserts.fail(
886            "Wi-Fi scan results/failure did not become available within 60s.")
887    # If there are multiple matches, we check for atleast one success.
888    for event in events:
889        if event["name"] == "WifiManagerScanResultsAvailable":
890            return True
891        elif event["name"] == "WifiManagerScanFailure":
892            ad.log.debug("Scan failure received")
893    return False
894
895
896def start_wifi_connection_scan_and_check_for_network(ad,
897                                                     network_ssid,
898                                                     max_tries=3):
899    """
900    Start connectivity scans & checks if the |network_ssid| is seen in
901    scan results. The method performs a max of |max_tries| connectivity scans
902    to find the network.
903
904    Args:
905        ad: An AndroidDevice object.
906        network_ssid: SSID of the network we are looking for.
907        max_tries: Number of scans to try.
908    Returns:
909        True: if network_ssid is found in scan results.
910        False: if network_ssid is not found in scan results.
911    """
912    for num_tries in range(max_tries):
913        if start_wifi_connection_scan_and_return_status(ad):
914            scan_results = ad.droid.wifiGetScanResults()
915            match_results = match_networks({WifiEnums.SSID_KEY: network_ssid},
916                                           scan_results)
917            if len(match_results) > 0:
918                return True
919    return False
920
921
922def start_wifi_connection_scan_and_ensure_network_found(
923        ad, network_ssid, max_tries=3):
924    """
925    Start connectivity scans & ensure the |network_ssid| is seen in
926    scan results. The method performs a max of |max_tries| connectivity scans
927    to find the network.
928    This method asserts on failure!
929
930    Args:
931        ad: An AndroidDevice object.
932        network_ssid: SSID of the network we are looking for.
933        max_tries: Number of scans to try.
934    """
935    ad.log.info("Starting scans to ensure %s is present", network_ssid)
936    assert_msg = "Failed to find " + network_ssid + " in scan results" \
937        " after " + str(max_tries) + " tries"
938    asserts.assert_true(
939        start_wifi_connection_scan_and_check_for_network(
940            ad, network_ssid, max_tries), assert_msg)
941
942
943def start_wifi_connection_scan_and_ensure_network_not_found(
944        ad, network_ssid, max_tries=3):
945    """
946    Start connectivity scans & ensure the |network_ssid| is not seen in
947    scan results. The method performs a max of |max_tries| connectivity scans
948    to find the network.
949    This method asserts on failure!
950
951    Args:
952        ad: An AndroidDevice object.
953        network_ssid: SSID of the network we are looking for.
954        max_tries: Number of scans to try.
955    """
956    ad.log.info("Starting scans to ensure %s is not present", network_ssid)
957    assert_msg = "Found " + network_ssid + " in scan results" \
958        " after " + str(max_tries) + " tries"
959    asserts.assert_false(
960        start_wifi_connection_scan_and_check_for_network(
961            ad, network_ssid, max_tries), assert_msg)
962
963
964def start_wifi_background_scan(ad, scan_setting):
965    """Starts wifi background scan.
966
967    Args:
968        ad: android_device object to initiate connection on.
969        scan_setting: A dict representing the settings of the scan.
970
971    Returns:
972        If scan was started successfully, event data of success event is returned.
973    """
974    idx = ad.droid.wifiScannerStartBackgroundScan(scan_setting)
975    event = ad.ed.pop_event("WifiScannerScan{}onSuccess".format(idx),
976                            SHORT_TIMEOUT)
977    return event['data']
978
979
980def start_wifi_tethering(ad, ssid, password, band=None, hidden=None,
981                         security=None):
982    """Starts wifi tethering on an android_device.
983
984    Args:
985        ad: android_device to start wifi tethering on.
986        ssid: The SSID the soft AP should broadcast.
987        password: The password the soft AP should use.
988        band: The band the soft AP should be set on. It should be either
989            WifiEnums.WIFI_CONFIG_APBAND_2G or WifiEnums.WIFI_CONFIG_APBAND_5G.
990        hidden: boolean to indicate if the AP needs to be hidden or not.
991        security: security type of softap.
992
993    Returns:
994        No return value. Error checks in this function will raise test failure signals
995    """
996    config = {WifiEnums.SSID_KEY: ssid}
997    if password:
998        config[WifiEnums.PWD_KEY] = password
999    if band:
1000        config[WifiEnums.AP_BAND_KEY] = band
1001    if hidden:
1002        config[WifiEnums.HIDDEN_KEY] = hidden
1003    if security:
1004        config[WifiEnums.SECURITY] = security
1005    asserts.assert_true(ad.droid.wifiSetWifiApConfiguration(config),
1006                        "Failed to update WifiAp Configuration")
1007    ad.droid.wifiStartTrackingTetherStateChange()
1008    ad.droid.connectivityStartTethering(tel_defines.TETHERING_WIFI, False)
1009    try:
1010        ad.ed.pop_event("ConnectivityManagerOnTetheringStarted")
1011        ad.ed.wait_for_event("TetherStateChanged",
1012                             lambda x: x["data"]["ACTIVE_TETHER"], 30)
1013        ad.log.debug("Tethering started successfully.")
1014    except Empty:
1015        msg = "Failed to receive confirmation of wifi tethering starting"
1016        asserts.fail(msg)
1017    finally:
1018        ad.droid.wifiStopTrackingTetherStateChange()
1019
1020
1021def save_wifi_soft_ap_config(ad,
1022                             wifi_config,
1023                             band=None,
1024                             hidden=None,
1025                             security=None,
1026                             password=None,
1027                             channel=None,
1028                             max_clients=None,
1029                             shutdown_timeout_enable=None,
1030                             shutdown_timeout_millis=None,
1031                             client_control_enable=None,
1032                             allowedList=None,
1033                             blockedList=None,
1034                             bands=None,
1035                             channel_frequencys=None,
1036                             mac_randomization_setting=None,
1037                             bridged_opportunistic_shutdown_enabled=None,
1038                             ieee80211ax_enabled=None):
1039    """ Save a soft ap configuration and verified
1040    Args:
1041        ad: android_device to set soft ap configuration.
1042        wifi_config: a soft ap configuration object, at least include SSID.
1043        band: specifies the band for the soft ap.
1044        hidden: specifies the soft ap need to broadcast its SSID or not.
1045        security: specifies the security type for the soft ap.
1046        password: specifies the password for the soft ap.
1047        channel: specifies the channel for the soft ap.
1048        max_clients: specifies the maximum connected client number.
1049        shutdown_timeout_enable: specifies the auto shut down enable or not.
1050        shutdown_timeout_millis: specifies the shut down timeout value.
1051        client_control_enable: specifies the client control enable or not.
1052        allowedList: specifies allowed clients list.
1053        blockedList: specifies blocked clients list.
1054        bands: specifies the band list for the soft ap.
1055        channel_frequencys: specifies the channel frequency list for soft ap.
1056        mac_randomization_setting: specifies the mac randomization setting.
1057        bridged_opportunistic_shutdown_enabled: specifies the opportunistic
1058                shutdown enable or not.
1059        ieee80211ax_enabled: specifies the ieee80211ax enable or not.
1060    """
1061    if security and password:
1062        wifi_config[WifiEnums.SECURITY] = security
1063        wifi_config[WifiEnums.PWD_KEY] = password
1064    if hidden is not None:
1065        wifi_config[WifiEnums.HIDDEN_KEY] = hidden
1066    if max_clients is not None:
1067        wifi_config[WifiEnums.AP_MAXCLIENTS_KEY] = max_clients
1068    if shutdown_timeout_enable is not None:
1069        wifi_config[
1070            WifiEnums.AP_SHUTDOWNTIMEOUTENABLE_KEY] = shutdown_timeout_enable
1071    if shutdown_timeout_millis is not None:
1072        wifi_config[WifiEnums.AP_SHUTDOWNTIMEOUT_KEY] = shutdown_timeout_millis
1073    if client_control_enable is not None:
1074        wifi_config[WifiEnums.AP_CLIENTCONTROL_KEY] = client_control_enable
1075    if allowedList is not None:
1076        wifi_config[WifiEnums.AP_ALLOWEDLIST_KEY] = allowedList
1077    if blockedList is not None:
1078        wifi_config[WifiEnums.AP_BLOCKEDLIST_KEY] = blockedList
1079    if mac_randomization_setting is not None:
1080        wifi_config[WifiEnums.AP_MAC_RANDOMIZATION_SETTING_KEY
1081                ] = mac_randomization_setting
1082    if bridged_opportunistic_shutdown_enabled is not None:
1083        wifi_config[WifiEnums.AP_BRIDGED_OPPORTUNISTIC_SHUTDOWN_ENABLE_KEY
1084                ] = bridged_opportunistic_shutdown_enabled
1085    if ieee80211ax_enabled is not None:
1086       wifi_config[WifiEnums.AP_IEEE80211AX_ENABLED_KEY]= ieee80211ax_enabled
1087    if channel_frequencys is not None:
1088        wifi_config[WifiEnums.AP_CHANNEL_FREQUENCYS_KEY] = channel_frequencys
1089    elif bands is not None:
1090        wifi_config[WifiEnums.AP_BANDS_KEY] = bands
1091    elif band is not None:
1092        if channel is not None:
1093            wifi_config[WifiEnums.AP_BAND_KEY] = band
1094            wifi_config[WifiEnums.AP_CHANNEL_KEY] = channel
1095        else:
1096             wifi_config[WifiEnums.AP_BAND_KEY] = band
1097
1098    if WifiEnums.AP_CHANNEL_KEY in wifi_config and wifi_config[
1099            WifiEnums.AP_CHANNEL_KEY] == 0:
1100        del wifi_config[WifiEnums.AP_CHANNEL_KEY]
1101
1102    if WifiEnums.SECURITY in wifi_config and wifi_config[
1103            WifiEnums.SECURITY] == WifiEnums.SoftApSecurityType.OPEN:
1104        del wifi_config[WifiEnums.SECURITY]
1105        del wifi_config[WifiEnums.PWD_KEY]
1106
1107    asserts.assert_true(ad.droid.wifiSetWifiApConfiguration(wifi_config),
1108                        "Failed to set WifiAp Configuration")
1109
1110    wifi_ap = ad.droid.wifiGetApConfiguration()
1111    asserts.assert_true(
1112        wifi_ap[WifiEnums.SSID_KEY] == wifi_config[WifiEnums.SSID_KEY],
1113        "Hotspot SSID doesn't match")
1114    if WifiEnums.SECURITY in wifi_config:
1115        asserts.assert_true(
1116            wifi_ap[WifiEnums.SECURITY] == wifi_config[WifiEnums.SECURITY],
1117            "Hotspot Security doesn't match")
1118    if WifiEnums.PWD_KEY in wifi_config:
1119        asserts.assert_true(
1120            wifi_ap[WifiEnums.PWD_KEY] == wifi_config[WifiEnums.PWD_KEY],
1121            "Hotspot Password doesn't match")
1122
1123    if WifiEnums.HIDDEN_KEY in wifi_config:
1124        asserts.assert_true(
1125            wifi_ap[WifiEnums.HIDDEN_KEY] == wifi_config[WifiEnums.HIDDEN_KEY],
1126            "Hotspot hidden setting doesn't match")
1127
1128    if WifiEnums.AP_CHANNEL_KEY in wifi_config:
1129        asserts.assert_true(
1130            wifi_ap[WifiEnums.AP_CHANNEL_KEY] == wifi_config[
1131                WifiEnums.AP_CHANNEL_KEY], "Hotspot Channel doesn't match")
1132    if WifiEnums.AP_MAXCLIENTS_KEY in wifi_config:
1133        asserts.assert_true(
1134            wifi_ap[WifiEnums.AP_MAXCLIENTS_KEY] == wifi_config[
1135                WifiEnums.AP_MAXCLIENTS_KEY],
1136            "Hotspot Max Clients doesn't match")
1137    if WifiEnums.AP_SHUTDOWNTIMEOUTENABLE_KEY in wifi_config:
1138        asserts.assert_true(
1139            wifi_ap[WifiEnums.AP_SHUTDOWNTIMEOUTENABLE_KEY] == wifi_config[
1140                WifiEnums.AP_SHUTDOWNTIMEOUTENABLE_KEY],
1141            "Hotspot ShutDown feature flag doesn't match")
1142    if WifiEnums.AP_SHUTDOWNTIMEOUT_KEY in wifi_config:
1143        asserts.assert_true(
1144            wifi_ap[WifiEnums.AP_SHUTDOWNTIMEOUT_KEY] == wifi_config[
1145                WifiEnums.AP_SHUTDOWNTIMEOUT_KEY],
1146            "Hotspot ShutDown timeout setting doesn't match")
1147    if WifiEnums.AP_CLIENTCONTROL_KEY in wifi_config:
1148        asserts.assert_true(
1149            wifi_ap[WifiEnums.AP_CLIENTCONTROL_KEY] == wifi_config[
1150                WifiEnums.AP_CLIENTCONTROL_KEY],
1151            "Hotspot Client control flag doesn't match")
1152    if WifiEnums.AP_ALLOWEDLIST_KEY in wifi_config:
1153        asserts.assert_true(
1154            wifi_ap[WifiEnums.AP_ALLOWEDLIST_KEY] == wifi_config[
1155                WifiEnums.AP_ALLOWEDLIST_KEY],
1156            "Hotspot Allowed List doesn't match")
1157    if WifiEnums.AP_BLOCKEDLIST_KEY in wifi_config:
1158        asserts.assert_true(
1159            wifi_ap[WifiEnums.AP_BLOCKEDLIST_KEY] == wifi_config[
1160                WifiEnums.AP_BLOCKEDLIST_KEY],
1161            "Hotspot Blocked List doesn't match")
1162
1163    if WifiEnums.AP_MAC_RANDOMIZATION_SETTING_KEY in wifi_config:
1164        asserts.assert_true(
1165            wifi_ap[WifiEnums.AP_MAC_RANDOMIZATION_SETTING_KEY] == wifi_config[
1166                  WifiEnums.AP_MAC_RANDOMIZATION_SETTING_KEY],
1167            "Hotspot Mac randomization setting doesn't match")
1168
1169    if WifiEnums.AP_BRIDGED_OPPORTUNISTIC_SHUTDOWN_ENABLE_KEY in wifi_config:
1170        asserts.assert_true(
1171            wifi_ap[WifiEnums.AP_BRIDGED_OPPORTUNISTIC_SHUTDOWN_ENABLE_KEY] == wifi_config[
1172                  WifiEnums.AP_BRIDGED_OPPORTUNISTIC_SHUTDOWN_ENABLE_KEY],
1173            "Hotspot bridged shutdown enable setting doesn't match")
1174
1175    if WifiEnums.AP_IEEE80211AX_ENABLED_KEY in wifi_config:
1176        asserts.assert_true(
1177            wifi_ap[WifiEnums.AP_IEEE80211AX_ENABLED_KEY] == wifi_config[
1178                  WifiEnums.AP_IEEE80211AX_ENABLED_KEY],
1179            "Hotspot 80211 AX enable setting doesn't match")
1180
1181    if WifiEnums.AP_CHANNEL_FREQUENCYS_KEY in wifi_config:
1182        asserts.assert_true(
1183            wifi_ap[WifiEnums.AP_CHANNEL_FREQUENCYS_KEY] == wifi_config[
1184                  WifiEnums.AP_CHANNEL_FREQUENCYS_KEY],
1185            "Hotspot channels setting doesn't match")
1186
1187def start_wifi_tethering_saved_config(ad):
1188    """ Turn on wifi hotspot with a config that is already saved """
1189    ad.droid.wifiStartTrackingTetherStateChange()
1190    ad.droid.connectivityStartTethering(tel_defines.TETHERING_WIFI, False)
1191    try:
1192        ad.ed.pop_event("ConnectivityManagerOnTetheringStarted")
1193        ad.ed.wait_for_event("TetherStateChanged",
1194                             lambda x: x["data"]["ACTIVE_TETHER"], 30)
1195    except:
1196        asserts.fail("Didn't receive wifi tethering starting confirmation")
1197    finally:
1198        ad.droid.wifiStopTrackingTetherStateChange()
1199
1200
1201def stop_wifi_tethering(ad):
1202    """Stops wifi tethering on an android_device.
1203    Args:
1204        ad: android_device to stop wifi tethering on.
1205    """
1206    ad.droid.wifiStartTrackingTetherStateChange()
1207    ad.droid.connectivityStopTethering(tel_defines.TETHERING_WIFI)
1208    try:
1209        ad.ed.pop_event("WifiManagerApDisabled", 30)
1210        ad.ed.wait_for_event("TetherStateChanged",
1211                             lambda x: not x["data"]["ACTIVE_TETHER"], 30)
1212    except Empty:
1213        msg = "Failed to receive confirmation of wifi tethering stopping"
1214        asserts.fail(msg)
1215    finally:
1216        ad.droid.wifiStopTrackingTetherStateChange()
1217
1218
1219def toggle_wifi_and_wait_for_reconnection(ad,
1220                                          network,
1221                                          num_of_tries=1,
1222                                          assert_on_fail=True):
1223    """Toggle wifi state and then wait for Android device to reconnect to
1224    the provided wifi network.
1225
1226    This expects the device to be already connected to the provided network.
1227
1228    Logic steps are
1229     1. Ensure that we're connected to the network.
1230     2. Turn wifi off.
1231     3. Wait for 10 seconds.
1232     4. Turn wifi on.
1233     5. Wait for the "connected" event, then confirm the connected ssid is the
1234        one requested.
1235
1236    Args:
1237        ad: android_device object to initiate connection on.
1238        network: A dictionary representing the network to await connection. The
1239                 dictionary must have the key "SSID".
1240        num_of_tries: An integer that is the number of times to try before
1241                      delaring failure. Default is 1.
1242        assert_on_fail: If True, error checks in this function will raise test
1243                        failure signals.
1244
1245    Returns:
1246        If assert_on_fail is False, function returns True if the toggle was
1247        successful, False otherwise. If assert_on_fail is True, no return value.
1248    """
1249    return _assert_on_fail_handler(_toggle_wifi_and_wait_for_reconnection,
1250                                   assert_on_fail,
1251                                   ad,
1252                                   network,
1253                                   num_of_tries=num_of_tries)
1254
1255
1256def _toggle_wifi_and_wait_for_reconnection(ad, network, num_of_tries=3):
1257    """Toggle wifi state and then wait for Android device to reconnect to
1258    the provided wifi network.
1259
1260    This expects the device to be already connected to the provided network.
1261
1262    Logic steps are
1263     1. Ensure that we're connected to the network.
1264     2. Turn wifi off.
1265     3. Wait for 10 seconds.
1266     4. Turn wifi on.
1267     5. Wait for the "connected" event, then confirm the connected ssid is the
1268        one requested.
1269
1270    This will directly fail a test if anything goes wrong.
1271
1272    Args:
1273        ad: android_device object to initiate connection on.
1274        network: A dictionary representing the network to await connection. The
1275                 dictionary must have the key "SSID".
1276        num_of_tries: An integer that is the number of times to try before
1277                      delaring failure. Default is 1.
1278    """
1279    expected_ssid = network[WifiEnums.SSID_KEY]
1280    # First ensure that we're already connected to the provided network.
1281    verify_con = {WifiEnums.SSID_KEY: expected_ssid}
1282    verify_wifi_connection_info(ad, verify_con)
1283    # Now toggle wifi state and wait for the connection event.
1284    wifi_toggle_state(ad, False)
1285    time.sleep(10)
1286    wifi_toggle_state(ad, True)
1287    ad.droid.wifiStartTrackingStateChange()
1288    try:
1289        connect_result = None
1290        for i in range(num_of_tries):
1291            try:
1292                connect_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED,
1293                                                 30)
1294                break
1295            except Empty:
1296                pass
1297        asserts.assert_true(
1298            connect_result, "Failed to connect to Wi-Fi network %s on %s" %
1299            (network, ad.serial))
1300        logging.debug("Connection result on %s: %s.", ad.serial,
1301                      connect_result)
1302        actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
1303        asserts.assert_equal(
1304            actual_ssid, expected_ssid, "Connected to the wrong network on %s."
1305            "Expected %s, but got %s." %
1306            (ad.serial, expected_ssid, actual_ssid))
1307        logging.info("Connected to Wi-Fi network %s on %s", actual_ssid,
1308                     ad.serial)
1309    finally:
1310        ad.droid.wifiStopTrackingStateChange()
1311
1312
1313def wait_for_connect(ad,
1314                     expected_ssid=None,
1315                     expected_id=None,
1316                     tries=2,
1317                     assert_on_fail=True):
1318    """Wait for a connect event.
1319
1320    This will directly fail a test if anything goes wrong.
1321
1322    Args:
1323        ad: An Android device object.
1324        expected_ssid: SSID of the network to connect to.
1325        expected_id: Network Id of the network to connect to.
1326        tries: An integer that is the number of times to try before failing.
1327        assert_on_fail: If True, error checks in this function will raise test
1328                        failure signals.
1329
1330    Returns:
1331        Returns a value only if assert_on_fail is false.
1332        Returns True if the connection was successful, False otherwise.
1333    """
1334    return _assert_on_fail_handler(_wait_for_connect, assert_on_fail, ad,
1335                                   expected_ssid, expected_id, tries)
1336
1337
1338def _wait_for_connect(ad, expected_ssid=None, expected_id=None, tries=2):
1339    """Wait for a connect event.
1340
1341    Args:
1342        ad: An Android device object.
1343        expected_ssid: SSID of the network to connect to.
1344        expected_id: Network Id of the network to connect to.
1345        tries: An integer that is the number of times to try before failing.
1346    """
1347    ad.droid.wifiStartTrackingStateChange()
1348    try:
1349        connect_result = _wait_for_connect_event(ad,
1350                                                 ssid=expected_ssid,
1351                                                 id=expected_id,
1352                                                 tries=tries)
1353        asserts.assert_true(
1354            connect_result,
1355            "Failed to connect to Wi-Fi network %s" % expected_ssid)
1356        ad.log.debug("Wi-Fi connection result: %s.", connect_result)
1357        actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
1358        if expected_ssid:
1359            asserts.assert_equal(actual_ssid, expected_ssid,
1360                                 "Connected to the wrong network")
1361        actual_id = connect_result['data'][WifiEnums.NETID_KEY]
1362        if expected_id:
1363            asserts.assert_equal(actual_id, expected_id,
1364                                 "Connected to the wrong network")
1365        ad.log.info("Connected to Wi-Fi network %s.", actual_ssid)
1366    except Empty:
1367        asserts.fail("Failed to start connection process to %s" %
1368                     expected_ssid)
1369    except Exception as error:
1370        ad.log.error("Failed to connect to %s with error %s", expected_ssid,
1371                     error)
1372        raise signals.TestFailure("Failed to connect to %s network" %
1373                                  expected_ssid)
1374    finally:
1375        ad.droid.wifiStopTrackingStateChange()
1376
1377
1378def _wait_for_connect_event(ad, ssid=None, id=None, tries=1):
1379    """Wait for a connect event on queue and pop when available.
1380
1381    Args:
1382        ad: An Android device object.
1383        ssid: SSID of the network to connect to.
1384        id: Network Id of the network to connect to.
1385        tries: An integer that is the number of times to try before failing.
1386
1387    Returns:
1388        A dict with details of the connection data, which looks like this:
1389        {
1390         'time': 1485460337798,
1391         'name': 'WifiNetworkConnected',
1392         'data': {
1393                  'rssi': -27,
1394                  'is_24ghz': True,
1395                  'mac_address': '02:00:00:00:00:00',
1396                  'network_id': 1,
1397                  'BSSID': '30:b5:c2:33:d3:fc',
1398                  'ip_address': 117483712,
1399                  'link_speed': 54,
1400                  'supplicant_state': 'completed',
1401                  'hidden_ssid': False,
1402                  'SSID': 'wh_ap1_2g',
1403                  'is_5ghz': False}
1404        }
1405
1406    """
1407    conn_result = None
1408
1409    # If ssid and network id is None, just wait for any connect event.
1410    if id is None and ssid is None:
1411        for i in range(tries):
1412            try:
1413                conn_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED,
1414                                              30)
1415                break
1416            except Empty:
1417                pass
1418    else:
1419        # If ssid or network id is specified, wait for specific connect event.
1420        for i in range(tries):
1421            try:
1422                conn_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED,
1423                                              30)
1424                if id and conn_result['data'][WifiEnums.NETID_KEY] == id:
1425                    break
1426                elif ssid and conn_result['data'][WifiEnums.SSID_KEY] == ssid:
1427                    break
1428            except Empty:
1429                pass
1430
1431    return conn_result
1432
1433
1434def wait_for_disconnect(ad, timeout=10):
1435    """Wait for a disconnect event within the specified timeout.
1436
1437    Args:
1438        ad: Android device object.
1439        timeout: Timeout in seconds.
1440
1441    """
1442    try:
1443        ad.droid.wifiStartTrackingStateChange()
1444        event = ad.ed.pop_event("WifiNetworkDisconnected", timeout)
1445    except Empty:
1446        raise signals.TestFailure("Device did not disconnect from the network")
1447    finally:
1448        ad.droid.wifiStopTrackingStateChange()
1449
1450
1451def ensure_no_disconnect(ad, duration=10):
1452    """Ensure that there is no disconnect for the specified duration.
1453
1454    Args:
1455        ad: Android device object.
1456        duration: Duration in seconds.
1457
1458    """
1459    try:
1460        ad.droid.wifiStartTrackingStateChange()
1461        event = ad.ed.pop_event("WifiNetworkDisconnected", duration)
1462        raise signals.TestFailure("Device disconnected from the network")
1463    except Empty:
1464        pass
1465    finally:
1466        ad.droid.wifiStopTrackingStateChange()
1467
1468
1469def connect_to_wifi_network(ad,
1470                            network,
1471                            assert_on_fail=True,
1472                            check_connectivity=True,
1473                            hidden=False):
1474    """Connection logic for open and psk wifi networks.
1475
1476    Args:
1477        ad: AndroidDevice to use for connection
1478        network: network info of the network to connect to
1479        assert_on_fail: If true, errors from wifi_connect will raise
1480                        test failure signals.
1481        hidden: Is the Wifi network hidden.
1482    """
1483    if hidden:
1484        start_wifi_connection_scan_and_ensure_network_not_found(
1485            ad, network[WifiEnums.SSID_KEY])
1486    else:
1487        start_wifi_connection_scan_and_ensure_network_found(
1488            ad, network[WifiEnums.SSID_KEY])
1489    wifi_connect(ad,
1490                 network,
1491                 num_of_tries=3,
1492                 assert_on_fail=assert_on_fail,
1493                 check_connectivity=check_connectivity)
1494
1495
1496def connect_to_wifi_network_with_id(ad, network_id, network_ssid):
1497    """Connect to the given network using network id and verify SSID.
1498
1499    Args:
1500        network_id: int Network Id of the network.
1501        network_ssid: string SSID of the network.
1502
1503    Returns: True if connect using network id was successful;
1504             False otherwise.
1505
1506    """
1507    start_wifi_connection_scan_and_ensure_network_found(ad, network_ssid)
1508    wifi_connect_by_id(ad, network_id)
1509    connect_data = ad.droid.wifiGetConnectionInfo()
1510    connect_ssid = connect_data[WifiEnums.SSID_KEY]
1511    ad.log.debug("Expected SSID = %s Connected SSID = %s" %
1512                 (network_ssid, connect_ssid))
1513    if connect_ssid != network_ssid:
1514        return False
1515    return True
1516
1517
1518def wifi_connect(ad,
1519                 network,
1520                 num_of_tries=1,
1521                 assert_on_fail=True,
1522                 check_connectivity=True):
1523    """Connect an Android device to a wifi network.
1524
1525    Initiate connection to a wifi network, wait for the "connected" event, then
1526    confirm the connected ssid is the one requested.
1527
1528    This will directly fail a test if anything goes wrong.
1529
1530    Args:
1531        ad: android_device object to initiate connection on.
1532        network: A dictionary representing the network to connect to. The
1533                 dictionary must have the key "SSID".
1534        num_of_tries: An integer that is the number of times to try before
1535                      delaring failure. Default is 1.
1536        assert_on_fail: If True, error checks in this function will raise test
1537                        failure signals.
1538
1539    Returns:
1540        Returns a value only if assert_on_fail is false.
1541        Returns True if the connection was successful, False otherwise.
1542    """
1543    return _assert_on_fail_handler(_wifi_connect,
1544                                   assert_on_fail,
1545                                   ad,
1546                                   network,
1547                                   num_of_tries=num_of_tries,
1548                                   check_connectivity=check_connectivity)
1549
1550
1551def _wifi_connect(ad, network, num_of_tries=1, check_connectivity=True):
1552    """Connect an Android device to a wifi network.
1553
1554    Initiate connection to a wifi network, wait for the "connected" event, then
1555    confirm the connected ssid is the one requested.
1556
1557    This will directly fail a test if anything goes wrong.
1558
1559    Args:
1560        ad: android_device object to initiate connection on.
1561        network: A dictionary representing the network to connect to. The
1562                 dictionary must have the key "SSID".
1563        num_of_tries: An integer that is the number of times to try before
1564                      delaring failure. Default is 1.
1565    """
1566    asserts.assert_true(
1567        WifiEnums.SSID_KEY in network,
1568        "Key '%s' must be present in network definition." % WifiEnums.SSID_KEY)
1569    ad.droid.wifiStartTrackingStateChange()
1570    expected_ssid = network[WifiEnums.SSID_KEY]
1571    ad.droid.wifiConnectByConfig(network)
1572    ad.log.info("Starting connection process to %s", expected_ssid)
1573    try:
1574        event = ad.ed.pop_event(wifi_constants.CONNECT_BY_CONFIG_SUCCESS, 30)
1575        connect_result = _wait_for_connect_event(ad,
1576                                                 ssid=expected_ssid,
1577                                                 tries=num_of_tries)
1578        asserts.assert_true(
1579            connect_result, "Failed to connect to Wi-Fi network %s on %s" %
1580            (network, ad.serial))
1581        ad.log.debug("Wi-Fi connection result: %s.", connect_result)
1582        actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
1583        asserts.assert_equal(
1584            actual_ssid, expected_ssid,
1585            "Connected to the wrong network on %s." % ad.serial)
1586        ad.log.info("Connected to Wi-Fi network %s.", actual_ssid)
1587
1588        if check_connectivity:
1589            internet = validate_connection(ad, DEFAULT_PING_ADDR)
1590            if not internet:
1591                raise signals.TestFailure(
1592                    "Failed to connect to internet on %s" % expected_ssid)
1593    except Empty:
1594        asserts.fail("Failed to start connection process to %s on %s" %
1595                     (network, ad.serial))
1596    except Exception as error:
1597        ad.log.error("Failed to connect to %s with error %s", expected_ssid,
1598                     error)
1599        raise signals.TestFailure("Failed to connect to %s network" % network)
1600
1601    finally:
1602        ad.droid.wifiStopTrackingStateChange()
1603
1604
1605def wifi_connect_by_id(ad, network_id, num_of_tries=3, assert_on_fail=True):
1606    """Connect an Android device to a wifi network using network Id.
1607
1608    Start connection to the wifi network, with the given network Id, wait for
1609    the "connected" event, then verify the connected network is the one requested.
1610
1611    This will directly fail a test if anything goes wrong.
1612
1613    Args:
1614        ad: android_device object to initiate connection on.
1615        network_id: Integer specifying the network id of the network.
1616        num_of_tries: An integer that is the number of times to try before
1617                      delaring failure. Default is 1.
1618        assert_on_fail: If True, error checks in this function will raise test
1619                        failure signals.
1620
1621    Returns:
1622        Returns a value only if assert_on_fail is false.
1623        Returns True if the connection was successful, False otherwise.
1624    """
1625    _assert_on_fail_handler(_wifi_connect_by_id, assert_on_fail, ad,
1626                            network_id, num_of_tries)
1627
1628
1629def _wifi_connect_by_id(ad, network_id, num_of_tries=1):
1630    """Connect an Android device to a wifi network using it's network id.
1631
1632    Start connection to the wifi network, with the given network id, wait for
1633    the "connected" event, then verify the connected network is the one requested.
1634
1635    Args:
1636        ad: android_device object to initiate connection on.
1637        network_id: Integer specifying the network id of the network.
1638        num_of_tries: An integer that is the number of times to try before
1639                      delaring failure. Default is 1.
1640    """
1641    ad.droid.wifiStartTrackingStateChange()
1642    # Clear all previous events.
1643    ad.ed.clear_all_events()
1644    ad.droid.wifiConnectByNetworkId(network_id)
1645    ad.log.info("Starting connection to network with id %d", network_id)
1646    try:
1647        event = ad.ed.pop_event(wifi_constants.CONNECT_BY_NETID_SUCCESS, 60)
1648        connect_result = _wait_for_connect_event(ad,
1649                                                 id=network_id,
1650                                                 tries=num_of_tries)
1651        asserts.assert_true(
1652            connect_result,
1653            "Failed to connect to Wi-Fi network using network id")
1654        ad.log.debug("Wi-Fi connection result: %s", connect_result)
1655        actual_id = connect_result['data'][WifiEnums.NETID_KEY]
1656        asserts.assert_equal(
1657            actual_id, network_id, "Connected to the wrong network on %s."
1658            "Expected network id = %d, but got %d." %
1659            (ad.serial, network_id, actual_id))
1660        expected_ssid = connect_result['data'][WifiEnums.SSID_KEY]
1661        ad.log.info("Connected to Wi-Fi network %s with %d network id.",
1662                    expected_ssid, network_id)
1663
1664        internet = validate_connection(ad, DEFAULT_PING_ADDR)
1665        if not internet:
1666            raise signals.TestFailure("Failed to connect to internet on %s" %
1667                                      expected_ssid)
1668    except Empty:
1669        asserts.fail("Failed to connect to network with id %d on %s" %
1670                     (network_id, ad.serial))
1671    except Exception as error:
1672        ad.log.error("Failed to connect to network with id %d with error %s",
1673                     network_id, error)
1674        raise signals.TestFailure("Failed to connect to network with network"
1675                                  " id %d" % network_id)
1676    finally:
1677        ad.droid.wifiStopTrackingStateChange()
1678
1679
1680def wifi_connect_using_network_request(ad,
1681                                       network,
1682                                       network_specifier,
1683                                       num_of_tries=3):
1684    """Connect an Android device to a wifi network using network request.
1685
1686    Trigger a network request with the provided network specifier,
1687    wait for the "onMatch" event, ensure that the scan results in "onMatch"
1688    event contain the specified network, then simulate the user granting the
1689    request with the specified network selected. Then wait for the "onAvailable"
1690    network callback indicating successful connection to network.
1691
1692    Args:
1693        ad: android_device object to initiate connection on.
1694        network_specifier: A dictionary representing the network specifier to
1695                           use.
1696        network: A dictionary representing the network to connect to. The
1697                 dictionary must have the key "SSID".
1698        num_of_tries: An integer that is the number of times to try before
1699                      delaring failure.
1700    Returns:
1701        key: Key corresponding to network request.
1702    """
1703    key = ad.droid.connectivityRequestWifiNetwork(network_specifier, 0)
1704    ad.log.info("Sent network request %s with %s " % (key, network_specifier))
1705    # Need a delay here because UI interaction should only start once wifi
1706    # starts processing the request.
1707    time.sleep(wifi_constants.NETWORK_REQUEST_CB_REGISTER_DELAY_SEC)
1708    _wait_for_wifi_connect_after_network_request(ad, network, key, num_of_tries)
1709    return key
1710
1711
1712def wait_for_wifi_connect_after_network_request(ad,
1713                                                network,
1714                                                key,
1715                                                num_of_tries=3,
1716                                                assert_on_fail=True):
1717    """
1718    Simulate and verify the connection flow after initiating the network
1719    request.
1720
1721    Wait for the "onMatch" event, ensure that the scan results in "onMatch"
1722    event contain the specified network, then simulate the user granting the
1723    request with the specified network selected. Then wait for the "onAvailable"
1724    network callback indicating successful connection to network.
1725
1726    Args:
1727        ad: android_device object to initiate connection on.
1728        network: A dictionary representing the network to connect to. The
1729                 dictionary must have the key "SSID".
1730        key: Key corresponding to network request.
1731        num_of_tries: An integer that is the number of times to try before
1732                      delaring failure.
1733        assert_on_fail: If True, error checks in this function will raise test
1734                        failure signals.
1735
1736    Returns:
1737        Returns a value only if assert_on_fail is false.
1738        Returns True if the connection was successful, False otherwise.
1739    """
1740    _assert_on_fail_handler(_wait_for_wifi_connect_after_network_request,
1741                            assert_on_fail, ad, network, key, num_of_tries)
1742
1743
1744def _wait_for_wifi_connect_after_network_request(ad, network, key, num_of_tries=3):
1745    """
1746    Simulate and verify the connection flow after initiating the network
1747    request.
1748
1749    Wait for the "onMatch" event, ensure that the scan results in "onMatch"
1750    event contain the specified network, then simulate the user granting the
1751    request with the specified network selected. Then wait for the "onAvailable"
1752    network callback indicating successful connection to network.
1753
1754    Args:
1755        ad: android_device object to initiate connection on.
1756        network: A dictionary representing the network to connect to. The
1757                 dictionary must have the key "SSID".
1758        key: Key corresponding to network request.
1759        num_of_tries: An integer that is the number of times to try before
1760                      delaring failure.
1761    """
1762    asserts.assert_true(
1763        WifiEnums.SSID_KEY in network,
1764        "Key '%s' must be present in network definition." % WifiEnums.SSID_KEY)
1765    ad.droid.wifiStartTrackingStateChange()
1766    expected_ssid = network[WifiEnums.SSID_KEY]
1767    ad.droid.wifiRegisterNetworkRequestMatchCallback()
1768    # Wait for the platform to scan and return a list of networks
1769    # matching the request
1770    try:
1771        matched_network = None
1772        for _ in [0, num_of_tries]:
1773            on_match_event = ad.ed.pop_event(
1774                wifi_constants.WIFI_NETWORK_REQUEST_MATCH_CB_ON_MATCH, 60)
1775            asserts.assert_true(on_match_event,
1776                                "Network request on match not received.")
1777            matched_scan_results = on_match_event["data"]
1778            ad.log.debug("Network request on match results %s",
1779                         matched_scan_results)
1780            matched_network = match_networks(
1781                {WifiEnums.SSID_KEY: network[WifiEnums.SSID_KEY]},
1782                matched_scan_results)
1783            ad.log.debug("Network request on match %s", matched_network)
1784            if matched_network:
1785                break
1786
1787        asserts.assert_true(matched_network,
1788                            "Target network %s not found" % network)
1789
1790        ad.droid.wifiSendUserSelectionForNetworkRequestMatch(network)
1791        ad.log.info("Sent user selection for network request %s",
1792                    expected_ssid)
1793
1794        # Wait for the platform to connect to the network.
1795        autils.wait_for_event_with_keys(
1796            ad, cconsts.EVENT_NETWORK_CALLBACK,
1797            60,
1798            (cconsts.NETWORK_CB_KEY_ID, key),
1799            (cconsts.NETWORK_CB_KEY_EVENT, cconsts.NETWORK_CB_AVAILABLE))
1800        on_capabilities_changed = autils.wait_for_event_with_keys(
1801            ad, cconsts.EVENT_NETWORK_CALLBACK,
1802            10,
1803            (cconsts.NETWORK_CB_KEY_ID, key),
1804            (cconsts.NETWORK_CB_KEY_EVENT,
1805             cconsts.NETWORK_CB_CAPABILITIES_CHANGED))
1806        connected_network = None
1807        # WifiInfo is attached to TransportInfo only in S.
1808        if ad.droid.isSdkAtLeastS():
1809            connected_network = (
1810                on_capabilities_changed["data"][
1811                    cconsts.NETWORK_CB_KEY_TRANSPORT_INFO]
1812            )
1813        else:
1814            connected_network = ad.droid.wifiGetConnectionInfo()
1815        ad.log.info("Connected to network %s", connected_network)
1816        asserts.assert_equal(
1817            connected_network[WifiEnums.SSID_KEY], expected_ssid,
1818            "Connected to the wrong network."
1819            "Expected %s, but got %s."
1820            % (network, connected_network))
1821    except Empty:
1822        asserts.fail("Failed to connect to %s" % expected_ssid)
1823    except Exception as error:
1824        ad.log.error("Failed to connect to %s with error %s" %
1825                     (expected_ssid, error))
1826        raise signals.TestFailure("Failed to connect to %s network" % network)
1827    finally:
1828        ad.droid.wifiStopTrackingStateChange()
1829
1830
1831def wifi_passpoint_connect(ad,
1832                           passpoint_network,
1833                           num_of_tries=1,
1834                           assert_on_fail=True):
1835    """Connect an Android device to a wifi network.
1836
1837    Initiate connection to a wifi network, wait for the "connected" event, then
1838    confirm the connected ssid is the one requested.
1839
1840    This will directly fail a test if anything goes wrong.
1841
1842    Args:
1843        ad: android_device object to initiate connection on.
1844        passpoint_network: SSID of the Passpoint network to connect to.
1845        num_of_tries: An integer that is the number of times to try before
1846                      delaring failure. Default is 1.
1847        assert_on_fail: If True, error checks in this function will raise test
1848                        failure signals.
1849
1850    Returns:
1851        If assert_on_fail is False, function returns network id, if the connect was
1852        successful, False otherwise. If assert_on_fail is True, no return value.
1853    """
1854    _assert_on_fail_handler(_wifi_passpoint_connect,
1855                            assert_on_fail,
1856                            ad,
1857                            passpoint_network,
1858                            num_of_tries=num_of_tries)
1859
1860
1861def _wifi_passpoint_connect(ad, passpoint_network, num_of_tries=1):
1862    """Connect an Android device to a wifi network.
1863
1864    Initiate connection to a wifi network, wait for the "connected" event, then
1865    confirm the connected ssid is the one requested.
1866
1867    This will directly fail a test if anything goes wrong.
1868
1869    Args:
1870        ad: android_device object to initiate connection on.
1871        passpoint_network: SSID of the Passpoint network to connect to.
1872        num_of_tries: An integer that is the number of times to try before
1873                      delaring failure. Default is 1.
1874    """
1875    ad.droid.wifiStartTrackingStateChange()
1876    expected_ssid = passpoint_network
1877    ad.log.info("Starting connection process to passpoint %s", expected_ssid)
1878
1879    try:
1880        connect_result = _wait_for_connect_event(ad, expected_ssid,
1881                                                 num_of_tries)
1882        asserts.assert_true(
1883            connect_result, "Failed to connect to WiFi passpoint network %s on"
1884            " %s" % (expected_ssid, ad.serial))
1885        ad.log.info("Wi-Fi connection result: %s.", connect_result)
1886        actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
1887        asserts.assert_equal(
1888            actual_ssid, expected_ssid,
1889            "Connected to the wrong network on %s." % ad.serial)
1890        ad.log.info("Connected to Wi-Fi passpoint network %s.", actual_ssid)
1891
1892        internet = validate_connection(ad, DEFAULT_PING_ADDR)
1893        if not internet:
1894            raise signals.TestFailure("Failed to connect to internet on %s" %
1895                                      expected_ssid)
1896    except Exception as error:
1897        ad.log.error("Failed to connect to passpoint network %s with error %s",
1898                     expected_ssid, error)
1899        raise signals.TestFailure("Failed to connect to %s passpoint network" %
1900                                  expected_ssid)
1901
1902    finally:
1903        ad.droid.wifiStopTrackingStateChange()
1904
1905
1906def delete_passpoint(ad, fqdn):
1907    """Delete a required Passpoint configuration."""
1908    try:
1909        ad.droid.removePasspointConfig(fqdn)
1910        return True
1911    except Exception as error:
1912        ad.log.error(
1913            "Failed to remove passpoint configuration with FQDN=%s "
1914            "and error=%s", fqdn, error)
1915        return False
1916
1917
1918def start_wifi_single_scan(ad, scan_setting):
1919    """Starts wifi single shot scan.
1920
1921    Args:
1922        ad: android_device object to initiate connection on.
1923        scan_setting: A dict representing the settings of the scan.
1924
1925    Returns:
1926        If scan was started successfully, event data of success event is returned.
1927    """
1928    idx = ad.droid.wifiScannerStartScan(scan_setting)
1929    event = ad.ed.pop_event("WifiScannerScan%sonSuccess" % idx, SHORT_TIMEOUT)
1930    ad.log.debug("Got event %s", event)
1931    return event['data']
1932
1933
1934def track_connection(ad, network_ssid, check_connection_count):
1935    """Track wifi connection to network changes for given number of counts
1936
1937    Args:
1938        ad: android_device object for forget network.
1939        network_ssid: network ssid to which connection would be tracked
1940        check_connection_count: Integer for maximum number network connection
1941                                check.
1942    Returns:
1943        True if connection to given network happen, else return False.
1944    """
1945    ad.droid.wifiStartTrackingStateChange()
1946    while check_connection_count > 0:
1947        connect_network = ad.ed.pop_event("WifiNetworkConnected", 120)
1948        ad.log.info("Connected to network %s", connect_network)
1949        if (WifiEnums.SSID_KEY in connect_network['data'] and
1950                connect_network['data'][WifiEnums.SSID_KEY] == network_ssid):
1951            return True
1952        check_connection_count -= 1
1953    ad.droid.wifiStopTrackingStateChange()
1954    return False
1955
1956
1957def get_scan_time_and_channels(wifi_chs, scan_setting, stime_channel):
1958    """Calculate the scan time required based on the band or channels in scan
1959    setting
1960
1961    Args:
1962        wifi_chs: Object of channels supported
1963        scan_setting: scan setting used for start scan
1964        stime_channel: scan time per channel
1965
1966    Returns:
1967        scan_time: time required for completing a scan
1968        scan_channels: channel used for scanning
1969    """
1970    scan_time = 0
1971    scan_channels = []
1972    if "band" in scan_setting and "channels" not in scan_setting:
1973        scan_channels = wifi_chs.band_to_freq(scan_setting["band"])
1974    elif "channels" in scan_setting and "band" not in scan_setting:
1975        scan_channels = scan_setting["channels"]
1976    scan_time = len(scan_channels) * stime_channel
1977    for channel in scan_channels:
1978        if channel in WifiEnums.DFS_5G_FREQUENCIES:
1979            scan_time += 132  #passive scan time on DFS
1980    return scan_time, scan_channels
1981
1982
1983def start_wifi_track_bssid(ad, track_setting):
1984    """Start tracking Bssid for the given settings.
1985
1986    Args:
1987      ad: android_device object.
1988      track_setting: Setting for which the bssid tracking should be started
1989
1990    Returns:
1991      If tracking started successfully, event data of success event is returned.
1992    """
1993    idx = ad.droid.wifiScannerStartTrackingBssids(
1994        track_setting["bssidInfos"], track_setting["apLostThreshold"])
1995    event = ad.ed.pop_event("WifiScannerBssid{}onSuccess".format(idx),
1996                            SHORT_TIMEOUT)
1997    return event['data']
1998
1999
2000def convert_pem_key_to_pkcs8(in_file, out_file):
2001    """Converts the key file generated by us to the format required by
2002    Android using openssl.
2003
2004    The input file must have the extension "pem". The output file must
2005    have the extension "der".
2006
2007    Args:
2008        in_file: The original key file.
2009        out_file: The full path to the converted key file, including
2010        filename.
2011    """
2012    asserts.assert_true(in_file.endswith(".pem"), "Input file has to be .pem.")
2013    asserts.assert_true(out_file.endswith(".der"),
2014                        "Output file has to be .der.")
2015    cmd = ("openssl pkcs8 -inform PEM -in {} -outform DER -out {} -nocrypt"
2016           " -topk8").format(in_file, out_file)
2017    utils.exe_cmd(cmd)
2018
2019
2020def validate_connection(ad,
2021                        ping_addr=DEFAULT_PING_ADDR,
2022                        wait_time=15,
2023                        ping_gateway=True):
2024    """Validate internet connection by pinging the address provided.
2025
2026    Args:
2027        ad: android_device object.
2028        ping_addr: address on internet for pinging.
2029        wait_time: wait for some time before validating connection
2030
2031    Returns:
2032        ping output if successful, NULL otherwise.
2033    """
2034    # wait_time to allow for DHCP to complete.
2035    for i in range(wait_time):
2036        if ad.droid.connectivityNetworkIsConnected(
2037        ) and ad.droid.connectivityGetIPv4DefaultGateway():
2038            break
2039        time.sleep(1)
2040    ping = False
2041    try:
2042        ping = ad.droid.httpPing(ping_addr)
2043        ad.log.info("Http ping result: %s.", ping)
2044    except:
2045        pass
2046    if not ping and ping_gateway:
2047        ad.log.info("Http ping failed. Pinging default gateway")
2048        gw = ad.droid.connectivityGetIPv4DefaultGateway()
2049        result = ad.adb.shell("ping -c 6 {}".format(gw))
2050        ad.log.info("Default gateway ping result: %s" % result)
2051        ping = False if "100% packet loss" in result else True
2052    return ping
2053
2054
2055#TODO(angli): This can only verify if an actual value is exactly the same.
2056# Would be nice to be able to verify an actual value is one of serveral.
2057def verify_wifi_connection_info(ad, expected_con):
2058    """Verifies that the information of the currently connected wifi network is
2059    as expected.
2060
2061    Args:
2062        expected_con: A dict representing expected key-value pairs for wifi
2063            connection. e.g. {"SSID": "test_wifi"}
2064    """
2065    current_con = ad.droid.wifiGetConnectionInfo()
2066    case_insensitive = ["BSSID", "supplicant_state"]
2067    ad.log.debug("Current connection: %s", current_con)
2068    for k, expected_v in expected_con.items():
2069        # Do not verify authentication related fields.
2070        if k == "password":
2071            continue
2072        msg = "Field %s does not exist in wifi connection info %s." % (
2073            k, current_con)
2074        if k not in current_con:
2075            raise signals.TestFailure(msg)
2076        actual_v = current_con[k]
2077        if k in case_insensitive:
2078            actual_v = actual_v.lower()
2079            expected_v = expected_v.lower()
2080        msg = "Expected %s to be %s, actual %s is %s." % (k, expected_v, k,
2081                                                          actual_v)
2082        if actual_v != expected_v:
2083            raise signals.TestFailure(msg)
2084
2085
2086def check_autoconnect_to_open_network(
2087        ad, conn_timeout=WIFI_CONNECTION_TIMEOUT_DEFAULT):
2088    """Connects to any open WiFI AP
2089     Args:
2090         timeout value in sec to wait for UE to connect to a WiFi AP
2091     Returns:
2092         True if UE connects to WiFi AP (supplicant_state = completed)
2093         False if UE fails to complete connection within WIFI_CONNECTION_TIMEOUT time.
2094    """
2095    if ad.droid.wifiCheckState():
2096        return True
2097    ad.droid.wifiToggleState()
2098    wifi_connection_state = None
2099    timeout = time.time() + conn_timeout
2100    while wifi_connection_state != "completed":
2101        wifi_connection_state = ad.droid.wifiGetConnectionInfo(
2102        )['supplicant_state']
2103        if time.time() > timeout:
2104            ad.log.warning("Failed to connect to WiFi AP")
2105            return False
2106    return True
2107
2108
2109def expand_enterprise_config_by_phase2(config):
2110    """Take an enterprise config and generate a list of configs, each with
2111    a different phase2 auth type.
2112
2113    Args:
2114        config: A dict representing enterprise config.
2115
2116    Returns
2117        A list of enterprise configs.
2118    """
2119    results = []
2120    phase2_types = WifiEnums.EapPhase2
2121    if config[WifiEnums.Enterprise.EAP] == WifiEnums.Eap.PEAP:
2122        # Skip unsupported phase2 types for PEAP.
2123        phase2_types = [WifiEnums.EapPhase2.GTC, WifiEnums.EapPhase2.MSCHAPV2]
2124    for phase2_type in phase2_types:
2125        # Skip a special case for passpoint TTLS.
2126        if (WifiEnums.Enterprise.FQDN in config
2127                and phase2_type == WifiEnums.EapPhase2.GTC):
2128            continue
2129        c = dict(config)
2130        c[WifiEnums.Enterprise.PHASE2] = phase2_type.value
2131        results.append(c)
2132    return results
2133
2134
2135def generate_eap_test_name(config, ad=None):
2136    """ Generates a test case name based on an EAP configuration.
2137
2138    Args:
2139        config: A dict representing an EAP credential.
2140        ad object: Redundant but required as the same param is passed
2141                   to test_func in run_generated_tests
2142
2143    Returns:
2144        A string representing the name of a generated EAP test case.
2145    """
2146    eap = WifiEnums.Eap
2147    eap_phase2 = WifiEnums.EapPhase2
2148    Ent = WifiEnums.Enterprise
2149    name = "test_connect-"
2150    eap_name = ""
2151    for e in eap:
2152        if e.value == config[Ent.EAP]:
2153            eap_name = e.name
2154            break
2155    if "peap0" in config[WifiEnums.SSID_KEY].lower():
2156        eap_name = "PEAP0"
2157    if "peap1" in config[WifiEnums.SSID_KEY].lower():
2158        eap_name = "PEAP1"
2159    name += eap_name
2160    if Ent.PHASE2 in config:
2161        for e in eap_phase2:
2162            if e.value == config[Ent.PHASE2]:
2163                name += "-{}".format(e.name)
2164                break
2165    return name
2166
2167
2168def group_attenuators(attenuators):
2169    """Groups a list of attenuators into attenuator groups for backward
2170    compatibility reasons.
2171
2172    Most legacy Wi-Fi setups have two attenuators each connected to a separate
2173    AP. The new Wi-Fi setup has four attenuators, each connected to one channel
2174    on an AP, so two of them are connected to one AP.
2175
2176    To make the existing scripts work in the new setup, when the script needs
2177    to attenuate one AP, it needs to set attenuation on both attenuators
2178    connected to the same AP.
2179
2180    This function groups attenuators properly so the scripts work in both
2181    legacy and new Wi-Fi setups.
2182
2183    Args:
2184        attenuators: A list of attenuator objects, either two or four in length.
2185
2186    Raises:
2187        signals.TestFailure is raised if the attenuator list does not have two
2188        or four objects.
2189    """
2190    attn0 = attenuator.AttenuatorGroup("AP0")
2191    attn1 = attenuator.AttenuatorGroup("AP1")
2192    # Legacy testbed setup has two attenuation channels.
2193    num_of_attns = len(attenuators)
2194    if num_of_attns == 2:
2195        attn0.add(attenuators[0])
2196        attn1.add(attenuators[1])
2197    elif num_of_attns == 4:
2198        attn0.add(attenuators[0])
2199        attn0.add(attenuators[1])
2200        attn1.add(attenuators[2])
2201        attn1.add(attenuators[3])
2202    else:
2203        asserts.fail(("Either two or four attenuators are required for this "
2204                      "test, but found %s") % num_of_attns)
2205    return [attn0, attn1]
2206
2207
2208def set_attns(attenuator, attn_val_name, roaming_attn=ROAMING_ATTN):
2209    """Sets attenuation values on attenuators used in this test.
2210
2211    Args:
2212        attenuator: The attenuator object.
2213        attn_val_name: Name of the attenuation value pair to use.
2214        roaming_attn: Dictionary specifying the attenuation params.
2215    """
2216    logging.info("Set attenuation values to %s", roaming_attn[attn_val_name])
2217    try:
2218        attenuator[0].set_atten(roaming_attn[attn_val_name][0])
2219        attenuator[1].set_atten(roaming_attn[attn_val_name][1])
2220        attenuator[2].set_atten(roaming_attn[attn_val_name][2])
2221        attenuator[3].set_atten(roaming_attn[attn_val_name][3])
2222    except:
2223        logging.exception("Failed to set attenuation values %s.",
2224                          attn_val_name)
2225        raise
2226
2227
2228def set_attns_steps(attenuators,
2229                    atten_val_name,
2230                    roaming_attn=ROAMING_ATTN,
2231                    steps=10,
2232                    wait_time=12):
2233    """Set attenuation values on attenuators used in this test. It will change
2234    the attenuation values linearly from current value to target value step by
2235    step.
2236
2237    Args:
2238        attenuators: The list of attenuator objects that you want to change
2239                     their attenuation value.
2240        atten_val_name: Name of the attenuation value pair to use.
2241        roaming_attn: Dictionary specifying the attenuation params.
2242        steps: Number of attenuator changes to reach the target value.
2243        wait_time: Sleep time for each change of attenuator.
2244    """
2245    logging.info("Set attenuation values to %s in %d step(s)",
2246                 roaming_attn[atten_val_name], steps)
2247    start_atten = [attenuator.get_atten() for attenuator in attenuators]
2248    target_atten = roaming_attn[atten_val_name]
2249    for current_step in range(steps):
2250        progress = (current_step + 1) / steps
2251        for i, attenuator in enumerate(attenuators):
2252            amount_since_start = (target_atten[i] - start_atten[i]) * progress
2253            attenuator.set_atten(round(start_atten[i] + amount_since_start))
2254        time.sleep(wait_time)
2255
2256
2257def trigger_roaming_and_validate(dut,
2258                                 attenuator,
2259                                 attn_val_name,
2260                                 expected_con,
2261                                 roaming_attn=ROAMING_ATTN):
2262    """Sets attenuators to trigger roaming and validate the DUT connected
2263    to the BSSID expected.
2264
2265    Args:
2266        attenuator: The attenuator object.
2267        attn_val_name: Name of the attenuation value pair to use.
2268        expected_con: The network information of the expected network.
2269        roaming_attn: Dictionary specifying the attenaution params.
2270    """
2271    expected_con = {
2272        WifiEnums.SSID_KEY: expected_con[WifiEnums.SSID_KEY],
2273        WifiEnums.BSSID_KEY: expected_con["bssid"],
2274    }
2275    set_attns_steps(attenuator, attn_val_name, roaming_attn)
2276
2277    verify_wifi_connection_info(dut, expected_con)
2278    expected_bssid = expected_con[WifiEnums.BSSID_KEY]
2279    logging.info("Roamed to %s successfully", expected_bssid)
2280    if not validate_connection(dut):
2281        raise signals.TestFailure("Fail to connect to internet on %s" %
2282                                  expected_bssid)
2283
2284
2285def create_softap_config():
2286    """Create a softap config with random ssid and password."""
2287    ap_ssid = "softap_" + utils.rand_ascii_str(8)
2288    ap_password = utils.rand_ascii_str(8)
2289    logging.info("softap setup: %s %s", ap_ssid, ap_password)
2290    config = {
2291        WifiEnums.SSID_KEY: ap_ssid,
2292        WifiEnums.PWD_KEY: ap_password,
2293    }
2294    return config
2295
2296
2297def start_softap_and_verify(ad, band):
2298    """Bring-up softap and verify AP mode and in scan results.
2299
2300    Args:
2301        band: The band to use for softAP.
2302
2303    Returns: dict, the softAP config.
2304
2305    """
2306    # Register before start the test.
2307    callbackId = ad.dut.droid.registerSoftApCallback()
2308    # Check softap info value is default
2309    frequency, bandwdith = get_current_softap_info(ad.dut, callbackId, True)
2310    asserts.assert_true(frequency == 0, "Softap frequency is not reset")
2311    asserts.assert_true(bandwdith == 0, "Softap bandwdith is not reset")
2312
2313    config = create_softap_config()
2314    start_wifi_tethering(ad.dut,
2315                         config[WifiEnums.SSID_KEY],
2316                         config[WifiEnums.PWD_KEY],
2317                         band=band)
2318    asserts.assert_true(ad.dut.droid.wifiIsApEnabled(),
2319                        "SoftAp is not reported as running")
2320    start_wifi_connection_scan_and_ensure_network_found(
2321        ad.dut_client, config[WifiEnums.SSID_KEY])
2322
2323    # Check softap info can get from callback succeed and assert value should be
2324    # valid.
2325    frequency, bandwdith = get_current_softap_info(ad.dut, callbackId, True)
2326    asserts.assert_true(frequency > 0, "Softap frequency is not valid")
2327    asserts.assert_true(bandwdith > 0, "Softap bandwdith is not valid")
2328    # Unregister callback
2329    ad.dut.droid.unregisterSoftApCallback(callbackId)
2330
2331    return config
2332
2333
2334def wait_for_expected_number_of_softap_clients(ad, callbackId,
2335                                               expected_num_of_softap_clients):
2336    """Wait for the number of softap clients to be updated as expected.
2337    Args:
2338        callbackId: Id of the callback associated with registering.
2339        expected_num_of_softap_clients: expected number of softap clients.
2340    """
2341    eventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str(
2342        callbackId) + wifi_constants.SOFTAP_NUMBER_CLIENTS_CHANGED
2343    clientData = ad.ed.pop_event(eventStr, SHORT_TIMEOUT)['data']
2344    clientCount = clientData[wifi_constants.SOFTAP_NUMBER_CLIENTS_CALLBACK_KEY]
2345    clientMacAddresses = clientData[
2346        wifi_constants.SOFTAP_CLIENTS_MACS_CALLBACK_KEY]
2347    asserts.assert_equal(
2348        clientCount, expected_num_of_softap_clients,
2349        "The number of softap clients doesn't match the expected number")
2350    asserts.assert_equal(
2351        len(clientMacAddresses), expected_num_of_softap_clients,
2352        "The number of mac addresses doesn't match the expected number")
2353    for macAddress in clientMacAddresses:
2354        asserts.assert_true(checkMacAddress(macAddress),
2355                            "An invalid mac address was returned")
2356
2357
2358def checkMacAddress(input):
2359    """Validate whether a string is a valid mac address or not.
2360
2361    Args:
2362        input: The string to validate.
2363
2364    Returns: True/False, returns true for a valid mac address and false otherwise.
2365    """
2366    macValidationRegex = "[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$"
2367    if re.match(macValidationRegex, input.lower()):
2368        return True
2369    return False
2370
2371
2372def wait_for_expected_softap_state(ad, callbackId, expected_softap_state):
2373    """Wait for the expected softap state change.
2374    Args:
2375        callbackId: Id of the callback associated with registering.
2376        expected_softap_state: The expected softap state.
2377    """
2378    eventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str(
2379        callbackId) + wifi_constants.SOFTAP_STATE_CHANGED
2380    asserts.assert_equal(
2381        ad.ed.pop_event(eventStr, SHORT_TIMEOUT)['data'][
2382            wifi_constants.SOFTAP_STATE_CHANGE_CALLBACK_KEY],
2383        expected_softap_state,
2384        "Softap state doesn't match with expected state")
2385
2386
2387def get_current_number_of_softap_clients(ad, callbackId):
2388    """pop up all of softap client updated event from queue.
2389    Args:
2390        callbackId: Id of the callback associated with registering.
2391
2392    Returns:
2393        If exist aleast callback, returns last updated number_of_softap_clients.
2394        Returns None when no any match callback event in queue.
2395    """
2396    eventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str(
2397        callbackId) + wifi_constants.SOFTAP_NUMBER_CLIENTS_CHANGED
2398    events = ad.ed.pop_all(eventStr)
2399    for event in events:
2400        num_of_clients = event['data'][
2401            wifi_constants.SOFTAP_NUMBER_CLIENTS_CALLBACK_KEY]
2402    if len(events) == 0:
2403        return None
2404    return num_of_clients
2405
2406
2407def get_current_softap_info(ad, callbackId, need_to_wait):
2408    """pop up all of softap info changed event from queue.
2409    Args:
2410        callbackId: Id of the callback associated with registering.
2411        need_to_wait: Wait for the info callback event before pop all.
2412    Returns:
2413        Returns last updated information of softap.
2414    """
2415    eventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str(
2416        callbackId) + wifi_constants.SOFTAP_INFO_CHANGED
2417    ad.log.debug("softap info dump from eventStr %s", eventStr)
2418    frequency = 0
2419    bandwidth = 0
2420    if (need_to_wait):
2421        event = ad.ed.pop_event(eventStr, SHORT_TIMEOUT)
2422        frequency = event['data'][
2423            wifi_constants.SOFTAP_INFO_FREQUENCY_CALLBACK_KEY]
2424        bandwidth = event['data'][
2425            wifi_constants.SOFTAP_INFO_BANDWIDTH_CALLBACK_KEY]
2426        ad.log.info("softap info updated, frequency is %s, bandwidth is %s",
2427                    frequency, bandwidth)
2428
2429    events = ad.ed.pop_all(eventStr)
2430    for event in events:
2431        frequency = event['data'][
2432            wifi_constants.SOFTAP_INFO_FREQUENCY_CALLBACK_KEY]
2433        bandwidth = event['data'][
2434            wifi_constants.SOFTAP_INFO_BANDWIDTH_CALLBACK_KEY]
2435    ad.log.info("softap info, frequency is %s, bandwidth is %s", frequency,
2436                bandwidth)
2437    return frequency, bandwidth
2438
2439def get_current_softap_infos(ad, callbackId, need_to_wait):
2440    """pop up all of softap info list changed event from queue.
2441    Args:
2442        callbackId: Id of the callback associated with registering.
2443        need_to_wait: Wait for the info callback event before pop all.
2444    Returns:
2445        Returns last updated informations of softap.
2446    """
2447    eventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str(
2448        callbackId) + wifi_constants.SOFTAP_INFOLIST_CHANGED
2449    ad.log.debug("softap info dump from eventStr %s", eventStr)
2450
2451    if (need_to_wait):
2452        event = ad.ed.pop_event(eventStr, SHORT_TIMEOUT)
2453        infos = event['data']
2454
2455    events = ad.ed.pop_all(eventStr)
2456    for event in events:
2457        infos = event['data']
2458
2459    for info in infos:
2460        frequency = info[
2461            wifi_constants.SOFTAP_INFO_FREQUENCY_CALLBACK_KEY]
2462        bandwidth = info[
2463            wifi_constants.SOFTAP_INFO_BANDWIDTH_CALLBACK_KEY]
2464        wifistandard = info[
2465            wifi_constants.SOFTAP_INFO_WIFISTANDARD_CALLBACK_KEY]
2466        bssid = info[
2467            wifi_constants.SOFTAP_INFO_BSSID_CALLBACK_KEY]
2468        ad.log.info(
2469                "softap info, freq:%s, bw:%s, wifistandard:%s, bssid:%s",
2470                frequency, bandwidth, wifistandard, bssid)
2471
2472    return infos
2473
2474def get_current_softap_capability(ad, callbackId, need_to_wait):
2475    """pop up all of softap info list changed event from queue.
2476    Args:
2477        callbackId: Id of the callback associated with registering.
2478        need_to_wait: Wait for the info callback event before pop all.
2479    Returns:
2480        Returns last updated capability of softap.
2481    """
2482    eventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str(
2483            callbackId) + wifi_constants.SOFTAP_CAPABILITY_CHANGED
2484    ad.log.debug("softap capability dump from eventStr %s", eventStr)
2485    if (need_to_wait):
2486        event = ad.ed.pop_event(eventStr, SHORT_TIMEOUT)
2487        capability = event['data']
2488
2489    events = ad.ed.pop_all(eventStr)
2490    for event in events:
2491        capability = event['data']
2492
2493    return capability
2494
2495def get_ssrdumps(ad):
2496    """Pulls dumps in the ssrdump dir
2497    Args:
2498        ad: android device object.
2499    """
2500    logs = ad.get_file_names("/data/vendor/ssrdump/")
2501    if logs:
2502        ad.log.info("Pulling ssrdumps %s", logs)
2503        log_path = os.path.join(ad.device_log_path, "SSRDUMPS_%s" % ad.serial)
2504        os.makedirs(log_path, exist_ok=True)
2505        ad.pull_files(logs, log_path)
2506    ad.adb.shell("find /data/vendor/ssrdump/ -type f -delete",
2507                 ignore_status=True)
2508
2509
2510def start_pcap(pcap, wifi_band, test_name):
2511    """Start packet capture in monitor mode.
2512
2513    Args:
2514        pcap: packet capture object
2515        wifi_band: '2g' or '5g' or 'dual'
2516        test_name: test name to be used for pcap file name
2517
2518    Returns:
2519        Dictionary with wifi band as key and the tuple
2520        (pcap Process object, log directory) as the value
2521    """
2522    log_dir = os.path.join(
2523        context.get_current_context().get_full_output_path(), 'PacketCapture')
2524    os.makedirs(log_dir, exist_ok=True)
2525    if wifi_band == 'dual':
2526        bands = [BAND_2G, BAND_5G]
2527    else:
2528        bands = [wifi_band]
2529    procs = {}
2530    for band in bands:
2531        proc = pcap.start_packet_capture(band, log_dir, test_name)
2532        procs[band] = (proc, os.path.join(log_dir, test_name))
2533    return procs
2534
2535
2536def stop_pcap(pcap, procs, test_status=None):
2537    """Stop packet capture in monitor mode.
2538
2539    Since, the pcap logs in monitor mode can be very large, we will
2540    delete them if they are not required. 'test_status' if True, will delete
2541    the pcap files. If False, we will keep them.
2542
2543    Args:
2544        pcap: packet capture object
2545        procs: dictionary returned by start_pcap
2546        test_status: status of the test case
2547    """
2548    for proc, fname in procs.values():
2549        pcap.stop_packet_capture(proc)
2550
2551    if test_status:
2552        shutil.rmtree(os.path.dirname(fname))
2553
2554
2555def verify_mac_not_found_in_pcap(ad, mac, packets):
2556    """Verify that a mac address is not found in the captured packets.
2557
2558    Args:
2559        ad: android device object
2560        mac: string representation of the mac address
2561        packets: packets obtained by rdpcap(pcap_fname)
2562    """
2563    for pkt in packets:
2564        logging.debug("Packet Summary = %s", pkt.summary())
2565        if mac in pkt.summary():
2566            asserts.fail("Device %s caught Factory MAC: %s in packet sniffer."
2567                         "Packet = %s" % (ad.serial, mac, pkt.show()))
2568
2569
2570def verify_mac_is_found_in_pcap(ad, mac, packets):
2571    """Verify that a mac address is found in the captured packets.
2572
2573    Args:
2574        ad: android device object
2575        mac: string representation of the mac address
2576        packets: packets obtained by rdpcap(pcap_fname)
2577    """
2578    for pkt in packets:
2579        if mac in pkt.summary():
2580            return
2581    asserts.fail("Did not find MAC = %s in packet sniffer."
2582                 "for device %s" % (mac, ad.serial))
2583
2584
2585def start_cnss_diags(ads, cnss_diag_file, pixel_models):
2586    for ad in ads:
2587        start_cnss_diag(ad, cnss_diag_file, pixel_models)
2588
2589
2590def start_cnss_diag(ad, cnss_diag_file, pixel_models):
2591    """Start cnss_diag to record extra wifi logs
2592
2593    Args:
2594        ad: android device object.
2595        cnss_diag_file: cnss diag config file to push to device.
2596        pixel_models: pixel devices.
2597    """
2598    if ad.model not in pixel_models:
2599        ad.log.info("Device not supported to collect pixel logger")
2600        return
2601    if ad.model in wifi_constants.DEVICES_USING_LEGACY_PROP:
2602        prop = wifi_constants.LEGACY_CNSS_DIAG_PROP
2603    else:
2604        prop = wifi_constants.CNSS_DIAG_PROP
2605    if ad.adb.getprop(prop) != 'true':
2606        if not int(
2607                ad.adb.shell("ls -l %s%s | wc -l" %
2608                             (CNSS_DIAG_CONFIG_PATH, CNSS_DIAG_CONFIG_FILE))):
2609            ad.adb.push("%s %s" % (cnss_diag_file, CNSS_DIAG_CONFIG_PATH))
2610        ad.adb.shell(
2611            "find /data/vendor/wifi/cnss_diag/wlan_logs/ -type f -delete",
2612            ignore_status=True)
2613        ad.adb.shell("setprop %s true" % prop, ignore_status=True)
2614
2615
2616def stop_cnss_diags(ads, pixel_models):
2617    for ad in ads:
2618        stop_cnss_diag(ad, pixel_models)
2619
2620
2621def stop_cnss_diag(ad, pixel_models):
2622    """Stops cnss_diag
2623
2624    Args:
2625        ad: android device object.
2626        pixel_models: pixel devices.
2627    """
2628    if ad.model not in pixel_models:
2629        ad.log.info("Device not supported to collect pixel logger")
2630        return
2631    if ad.model in wifi_constants.DEVICES_USING_LEGACY_PROP:
2632        prop = wifi_constants.LEGACY_CNSS_DIAG_PROP
2633    else:
2634        prop = wifi_constants.CNSS_DIAG_PROP
2635    ad.adb.shell("setprop %s false" % prop, ignore_status=True)
2636
2637
2638def get_cnss_diag_log(ad):
2639    """Pulls the cnss_diag logs in the wlan_logs dir
2640    Args:
2641        ad: android device object.
2642    """
2643    logs = ad.get_file_names("/data/vendor/wifi/cnss_diag/wlan_logs/")
2644    if logs:
2645        ad.log.info("Pulling cnss_diag logs %s", logs)
2646        log_path = os.path.join(ad.device_log_path, "CNSS_DIAG_%s" % ad.serial)
2647        os.makedirs(log_path, exist_ok=True)
2648        ad.pull_files(logs, log_path)
2649
2650
2651LinkProbeResult = namedtuple(
2652    'LinkProbeResult',
2653    ('is_success', 'stdout', 'elapsed_time', 'failure_reason'))
2654
2655
2656def send_link_probe(ad):
2657    """Sends a link probe to the currently connected AP, and returns whether the
2658    probe succeeded or not.
2659
2660    Args:
2661         ad: android device object
2662    Returns:
2663        LinkProbeResult namedtuple
2664    """
2665    stdout = ad.adb.shell('cmd wifi send-link-probe')
2666    asserts.assert_false('Error' in stdout or 'Exception' in stdout,
2667                         'Exception while sending link probe: ' + stdout)
2668
2669    is_success = False
2670    elapsed_time = None
2671    failure_reason = None
2672    if 'succeeded' in stdout:
2673        is_success = True
2674        elapsed_time = next(
2675            (int(token) for token in stdout.split() if token.isdigit()), None)
2676    elif 'failed with reason' in stdout:
2677        failure_reason = next(
2678            (int(token) for token in stdout.split() if token.isdigit()), None)
2679    else:
2680        asserts.fail('Unexpected link probe result: ' + stdout)
2681
2682    return LinkProbeResult(is_success=is_success,
2683                           stdout=stdout,
2684                           elapsed_time=elapsed_time,
2685                           failure_reason=failure_reason)
2686
2687
2688def send_link_probes(ad, num_probes, delay_sec):
2689    """Sends a sequence of link probes to the currently connected AP, and
2690    returns whether the probes succeeded or not.
2691
2692    Args:
2693         ad: android device object
2694         num_probes: number of probes to perform
2695         delay_sec: delay time between probes, in seconds
2696    Returns:
2697        List[LinkProbeResult] one LinkProbeResults for each probe
2698    """
2699    logging.info('Sending link probes')
2700    results = []
2701    for _ in range(num_probes):
2702        # send_link_probe() will also fail the test if it sees an exception
2703        # in the stdout of the adb shell command
2704        result = send_link_probe(ad)
2705        logging.info('link probe results: ' + str(result))
2706        results.append(result)
2707        time.sleep(delay_sec)
2708
2709    return results
2710
2711
2712def ap_setup(test, index, ap, network, bandwidth=80, channel=6):
2713    """Set up the AP with provided network info.
2714
2715        Args:
2716            test: the calling test class object.
2717            index: int, index of the AP.
2718            ap: access_point object of the AP.
2719            network: dict with information of the network, including ssid,
2720                     password and bssid.
2721            bandwidth: the operation bandwidth for the AP, default 80MHz.
2722            channel: the channel number for the AP.
2723        Returns:
2724            brconfigs: the bridge interface configs
2725        """
2726    bss_settings = []
2727    ssid = network[WifiEnums.SSID_KEY]
2728    test.access_points[index].close()
2729    time.sleep(5)
2730
2731    # Configure AP as required.
2732    if "password" in network.keys():
2733        password = network["password"]
2734        security = hostapd_security.Security(security_mode="wpa",
2735                                             password=password)
2736    else:
2737        security = hostapd_security.Security(security_mode=None, password=None)
2738    config = hostapd_ap_preset.create_ap_preset(channel=channel,
2739                                                ssid=ssid,
2740                                                security=security,
2741                                                bss_settings=bss_settings,
2742                                                vht_bandwidth=bandwidth,
2743                                                profile_name='whirlwind',
2744                                                iface_wlan_2g=ap.wlan_2g,
2745                                                iface_wlan_5g=ap.wlan_5g)
2746    ap.start_ap(config)
2747    logging.info("AP started on channel {} with SSID {}".format(channel, ssid))
2748
2749
2750def turn_ap_off(test, AP):
2751    """Bring down hostapd on the Access Point.
2752    Args:
2753        test: The test class object.
2754        AP: int, indicating which AP to turn OFF.
2755    """
2756    hostapd_2g = test.access_points[AP - 1]._aps['wlan0'].hostapd
2757    if hostapd_2g.is_alive():
2758        hostapd_2g.stop()
2759        logging.debug('Turned WLAN0 AP%d off' % AP)
2760    hostapd_5g = test.access_points[AP - 1]._aps['wlan1'].hostapd
2761    if hostapd_5g.is_alive():
2762        hostapd_5g.stop()
2763        logging.debug('Turned WLAN1 AP%d off' % AP)
2764
2765
2766def turn_ap_on(test, AP):
2767    """Bring up hostapd on the Access Point.
2768    Args:
2769        test: The test class object.
2770        AP: int, indicating which AP to turn ON.
2771    """
2772    hostapd_2g = test.access_points[AP - 1]._aps['wlan0'].hostapd
2773    if not hostapd_2g.is_alive():
2774        hostapd_2g.start(hostapd_2g.config)
2775        logging.debug('Turned WLAN0 AP%d on' % AP)
2776    hostapd_5g = test.access_points[AP - 1]._aps['wlan1'].hostapd
2777    if not hostapd_5g.is_alive():
2778        hostapd_5g.start(hostapd_5g.config)
2779        logging.debug('Turned WLAN1 AP%d on' % AP)
2780
2781
2782def turn_location_off_and_scan_toggle_off(ad):
2783    """Turns off wifi location scans."""
2784    utils.set_location_service(ad, False)
2785    ad.droid.wifiScannerToggleAlwaysAvailable(False)
2786    msg = "Failed to turn off location service's scan."
2787    asserts.assert_true(not ad.droid.wifiScannerIsAlwaysAvailable(), msg)
2788
2789
2790def set_softap_channel(dut, ap_iface='wlan1', cs_count=10, channel=2462):
2791    """ Set SoftAP mode channel
2792
2793    Args:
2794        dut: android device object
2795        ap_iface: interface of SoftAP mode.
2796        cs_count: how many beacon frames before switch channel, default = 10
2797        channel: a wifi channel.
2798    """
2799    chan_switch_cmd = 'hostapd_cli -i {} chan_switch {} {}'
2800    chan_switch_cmd_show = chan_switch_cmd.format(ap_iface, cs_count, channel)
2801    dut.log.info('adb shell {}'.format(chan_switch_cmd_show))
2802    chan_switch_result = dut.adb.shell(
2803        chan_switch_cmd.format(ap_iface, cs_count, channel))
2804    if chan_switch_result == 'OK':
2805        dut.log.info('switch hotspot channel to {}'.format(channel))
2806        return chan_switch_result
2807
2808    asserts.fail("Failed to switch hotspot channel")
2809
2810def get_wlan0_link(dut):
2811    """ get wlan0 interface status"""
2812    get_wlan0 = 'wpa_cli -iwlan0 -g@android:wpa_wlan0 IFNAME=wlan0 status'
2813    out = dut.adb.shell(get_wlan0)
2814    out = dict(re.findall(r'(\S+)=(".*?"|\S+)', out))
2815    asserts.assert_true("ssid" in out,
2816                        "Client doesn't connect to any network")
2817    return out
2818
2819def verify_11ax_wifi_connection(ad, wifi6_supported_models, wifi6_ap):
2820    """Verify 11ax for wifi connection.
2821
2822    Args:
2823      ad: adndroid device object
2824      wifi6_supported_models: device supporting 11ax.
2825      wifi6_ap: if the AP supports 11ax.
2826    """
2827    if wifi6_ap and ad.model in wifi6_supported_models:
2828        logging.info("Verifying 11ax. Model: %s" % ad.model)
2829        asserts.assert_true(
2830            ad.droid.wifiGetConnectionStandard() ==
2831            wifi_constants.WIFI_STANDARD_11AX, "DUT did not connect to 11ax.")
2832
2833def verify_11ax_softap(dut, dut_client, wifi6_supported_models):
2834    """Verify 11ax SoftAp if devices support it.
2835
2836    Check if both DUT and DUT client supports 11ax, then SoftAp turns on
2837    with 11ax mode and DUT client can connect to it.
2838
2839    Args:
2840      dut: Softap device.
2841      dut_client: Client connecting to softap.
2842      wifi6_supported_models: List of device models supporting 11ax.
2843    """
2844    if dut.model in wifi6_supported_models and dut_client.model in wifi6_supported_models:
2845        logging.info(
2846            "Verifying 11ax softap. DUT model: %s, DUT Client model: %s",
2847            dut.model, dut_client.model)
2848        asserts.assert_true(
2849            dut_client.droid.wifiGetConnectionStandard() ==
2850            wifi_constants.WIFI_STANDARD_11AX,
2851            "DUT failed to start SoftAp in 11ax.")
2852
2853def check_available_channels_in_bands_2_5(dut, country_code):
2854    """Check if DUT is capable of enable BridgedAp.
2855    #TODO: Find a way to make this function flexible by taking an argument.
2856
2857    Args:
2858        country_code: country code, e.g., 'US', 'JP'.
2859    Returns:
2860        True: If DUT is capable of enable BridgedAp.
2861        False: If DUT is not capable of enable BridgedAp.
2862    """
2863    set_wifi_country_code(dut, country_code)
2864    country = dut.droid.wifiGetCountryCode()
2865    dut.log.info("DUT current country code : {}".format(country))
2866    # Wi-Fi ON and OFF to make sure country code take effet.
2867    wifi_toggle_state(dut, True)
2868    wifi_toggle_state(dut, False)
2869
2870    # Register SoftAp Callback and get SoftAp capability.
2871    callbackId = dut.droid.registerSoftApCallback()
2872    capability = get_current_softap_capability(dut, callbackId, True)
2873    dut.droid.unregisterSoftApCallback(callbackId)
2874
2875    if capability[wifi_constants.
2876                  SOFTAP_CAPABILITY_24GHZ_SUPPORTED_CHANNEL_LIST] and \
2877        capability[wifi_constants.
2878                   SOFTAP_CAPABILITY_5GHZ_SUPPORTED_CHANNEL_LIST]:
2879        return True
2880    return False
2881
2882
2883@retry(tries=5, delay=2)
2884def validate_ping_between_two_clients(dut1, dut2):
2885    """Make 2 DUT ping each other.
2886
2887    Args:
2888        dut1: An AndroidDevice object.
2889        dut2: An AndroidDevice object.
2890    """
2891    # Get DUTs' IPv4 addresses.
2892    dut1_ip = ""
2893    dut2_ip = ""
2894    try:
2895        dut1_ip = dut1.droid.connectivityGetIPv4Addresses('wlan0')[0]
2896    except IndexError as e:
2897        dut1.log.info(
2898            "{} has no Wi-Fi connection, cannot get IPv4 address."
2899            .format(dut1.serial))
2900    try:
2901        dut2_ip = dut2.droid.connectivityGetIPv4Addresses('wlan0')[0]
2902    except IndexError as e:
2903        dut2.log.info(
2904            "{} has no Wi-Fi connection, cannot get IPv4 address."
2905            .format(dut2.serial))
2906    # Test fail if not able to obtain two DUT's IPv4 addresses.
2907    asserts.assert_true(dut1_ip and dut2_ip,
2908                        "Ping failed because no DUT's IPv4 address")
2909
2910    dut1.log.info("{} IPv4 addresses : {}".format(dut1.serial, dut1_ip))
2911    dut2.log.info("{} IPv4 addresses : {}".format(dut2.serial, dut2_ip))
2912
2913    # Two clients ping each other
2914    dut1.log.info("{} ping {}".format(dut1_ip, dut2_ip))
2915    asserts.assert_true(
2916        utils.adb_shell_ping(dut1, count=10, dest_ip=dut2_ip,
2917                             timeout=20),
2918        "%s ping %s failed" % (dut1.serial, dut2_ip))
2919
2920    dut2.log.info("{} ping {}".format(dut2_ip, dut1_ip))
2921    asserts.assert_true(
2922        utils.adb_shell_ping(dut2, count=10, dest_ip=dut1_ip,
2923                             timeout=20),
2924        "%s ping %s failed" % (dut2.serial, dut1_ip))
2925
2926