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