• 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 shutil
20import time
21
22from collections import namedtuple
23from enum import IntEnum
24from queue import Empty
25
26from acts import asserts
27from acts import context
28from acts import signals
29from acts import utils
30from acts.controllers import attenuator
31from acts.controllers.ap_lib import hostapd_security
32from acts.controllers.ap_lib import hostapd_ap_preset
33from acts.controllers.ap_lib.hostapd_constants import BAND_2G
34from acts.controllers.ap_lib.hostapd_constants import BAND_5G
35from acts.test_utils.wifi import wifi_constants
36from acts.test_utils.tel import tel_defines
37
38# Default timeout used for reboot, toggle WiFi and Airplane mode,
39# for the system to settle down after the operation.
40DEFAULT_TIMEOUT = 10
41# Number of seconds to wait for events that are supposed to happen quickly.
42# Like onSuccess for start background scan and confirmation on wifi state
43# change.
44SHORT_TIMEOUT = 30
45ROAMING_TIMEOUT = 30
46WIFI_CONNECTION_TIMEOUT_DEFAULT = 30
47WIFI_ABNORMAL_CONNECTION_TIME = 10
48# Speed of light in m/s.
49SPEED_OF_LIGHT = 299792458
50
51DEFAULT_PING_ADDR = "https://www.google.com/robots.txt"
52
53roaming_attn = {
54        "AP1_on_AP2_off": [
55            0,
56            0,
57            95,
58            95
59        ],
60        "AP1_off_AP2_on": [
61            95,
62            95,
63            0,
64            0
65        ],
66        "default": [
67            0,
68            0,
69            0,
70            0
71        ]
72    }
73
74
75class WifiEnums():
76
77    SSID_KEY = "SSID"
78    SSID_PATTERN_KEY = "ssidPattern"
79    NETID_KEY = "network_id"
80    BSSID_KEY = "BSSID"
81    BSSID_PATTERN_KEY = "bssidPattern"
82    PWD_KEY = "password"
83    frequency_key = "frequency"
84    APBAND_KEY = "apBand"
85    HIDDEN_KEY = "hiddenSSID"
86    IS_APP_INTERACTION_REQUIRED = "isAppInteractionRequired"
87    IS_USER_INTERACTION_REQUIRED = "isUserInteractionRequired"
88    IS_METERED = "isMetered"
89    PRIORITY = "priority"
90    SECURITY = "security"
91
92    WIFI_CONFIG_APBAND_2G = 0
93    WIFI_CONFIG_APBAND_5G = 1
94    WIFI_CONFIG_APBAND_AUTO = -1
95
96    WIFI_WPS_INFO_PBC = 0
97    WIFI_WPS_INFO_DISPLAY = 1
98    WIFI_WPS_INFO_KEYPAD = 2
99    WIFI_WPS_INFO_LABEL = 3
100    WIFI_WPS_INFO_INVALID = 4
101
102    class CountryCode():
103        CHINA = "CN"
104        JAPAN = "JP"
105        UK = "GB"
106        US = "US"
107        UNKNOWN = "UNKNOWN"
108
109    # Start of Macros for EAP
110    # EAP types
111    class Eap(IntEnum):
112        NONE = -1
113        PEAP = 0
114        TLS = 1
115        TTLS = 2
116        PWD = 3
117        SIM = 4
118        AKA = 5
119        AKA_PRIME = 6
120        UNAUTH_TLS = 7
121
122    # EAP Phase2 types
123    class EapPhase2(IntEnum):
124        NONE = 0
125        PAP = 1
126        MSCHAP = 2
127        MSCHAPV2 = 3
128        GTC = 4
129
130    class Enterprise:
131        # Enterprise Config Macros
132        EMPTY_VALUE = "NULL"
133        EAP = "eap"
134        PHASE2 = "phase2"
135        IDENTITY = "identity"
136        ANON_IDENTITY = "anonymous_identity"
137        PASSWORD = "password"
138        SUBJECT_MATCH = "subject_match"
139        ALTSUBJECT_MATCH = "altsubject_match"
140        DOM_SUFFIX_MATCH = "domain_suffix_match"
141        CLIENT_CERT = "client_cert"
142        CA_CERT = "ca_cert"
143        ENGINE = "engine"
144        ENGINE_ID = "engine_id"
145        PRIVATE_KEY_ID = "key_id"
146        REALM = "realm"
147        PLMN = "plmn"
148        FQDN = "FQDN"
149        FRIENDLY_NAME = "providerFriendlyName"
150        ROAMING_IDS = "roamingConsortiumIds"
151    # End of Macros for EAP
152
153    # Macros for wifi p2p.
154    WIFI_P2P_SERVICE_TYPE_ALL = 0
155    WIFI_P2P_SERVICE_TYPE_BONJOUR = 1
156    WIFI_P2P_SERVICE_TYPE_UPNP = 2
157    WIFI_P2P_SERVICE_TYPE_VENDOR_SPECIFIC = 255
158
159    class ScanResult:
160        CHANNEL_WIDTH_20MHZ = 0
161        CHANNEL_WIDTH_40MHZ = 1
162        CHANNEL_WIDTH_80MHZ = 2
163        CHANNEL_WIDTH_160MHZ = 3
164        CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4
165
166    # Macros for wifi rtt.
167    class RttType(IntEnum):
168        TYPE_ONE_SIDED = 1
169        TYPE_TWO_SIDED = 2
170
171    class RttPeerType(IntEnum):
172        PEER_TYPE_AP = 1
173        PEER_TYPE_STA = 2  # Requires NAN.
174        PEER_P2P_GO = 3
175        PEER_P2P_CLIENT = 4
176        PEER_NAN = 5
177
178    class RttPreamble(IntEnum):
179        PREAMBLE_LEGACY = 0x01
180        PREAMBLE_HT = 0x02
181        PREAMBLE_VHT = 0x04
182
183    class RttBW(IntEnum):
184        BW_5_SUPPORT = 0x01
185        BW_10_SUPPORT = 0x02
186        BW_20_SUPPORT = 0x04
187        BW_40_SUPPORT = 0x08
188        BW_80_SUPPORT = 0x10
189        BW_160_SUPPORT = 0x20
190
191    class Rtt(IntEnum):
192        STATUS_SUCCESS = 0
193        STATUS_FAILURE = 1
194        STATUS_FAIL_NO_RSP = 2
195        STATUS_FAIL_REJECTED = 3
196        STATUS_FAIL_NOT_SCHEDULED_YET = 4
197        STATUS_FAIL_TM_TIMEOUT = 5
198        STATUS_FAIL_AP_ON_DIFF_CHANNEL = 6
199        STATUS_FAIL_NO_CAPABILITY = 7
200        STATUS_ABORTED = 8
201        STATUS_FAIL_INVALID_TS = 9
202        STATUS_FAIL_PROTOCOL = 10
203        STATUS_FAIL_SCHEDULE = 11
204        STATUS_FAIL_BUSY_TRY_LATER = 12
205        STATUS_INVALID_REQ = 13
206        STATUS_NO_WIFI = 14
207        STATUS_FAIL_FTM_PARAM_OVERRIDE = 15
208
209        REASON_UNSPECIFIED = -1
210        REASON_NOT_AVAILABLE = -2
211        REASON_INVALID_LISTENER = -3
212        REASON_INVALID_REQUEST = -4
213
214    class RttParam:
215        device_type = "deviceType"
216        request_type = "requestType"
217        BSSID = "bssid"
218        channel_width = "channelWidth"
219        frequency = "frequency"
220        center_freq0 = "centerFreq0"
221        center_freq1 = "centerFreq1"
222        number_burst = "numberBurst"
223        interval = "interval"
224        num_samples_per_burst = "numSamplesPerBurst"
225        num_retries_per_measurement_frame = "numRetriesPerMeasurementFrame"
226        num_retries_per_FTMR = "numRetriesPerFTMR"
227        lci_request = "LCIRequest"
228        lcr_request = "LCRRequest"
229        burst_timeout = "burstTimeout"
230        preamble = "preamble"
231        bandwidth = "bandwidth"
232        margin = "margin"
233
234    RTT_MARGIN_OF_ERROR = {
235        RttBW.BW_80_SUPPORT: 2,
236        RttBW.BW_40_SUPPORT: 5,
237        RttBW.BW_20_SUPPORT: 5
238    }
239
240    # Macros as specified in the WifiScanner code.
241    WIFI_BAND_UNSPECIFIED = 0  # not specified
242    WIFI_BAND_24_GHZ = 1  # 2.4 GHz band
243    WIFI_BAND_5_GHZ = 2  # 5 GHz band without DFS channels
244    WIFI_BAND_5_GHZ_DFS_ONLY = 4  # 5 GHz band with DFS channels
245    WIFI_BAND_5_GHZ_WITH_DFS = 6  # 5 GHz band with DFS channels
246    WIFI_BAND_BOTH = 3  # both bands without DFS channels
247    WIFI_BAND_BOTH_WITH_DFS = 7  # both bands with DFS channels
248
249    REPORT_EVENT_AFTER_BUFFER_FULL = 0
250    REPORT_EVENT_AFTER_EACH_SCAN = 1
251    REPORT_EVENT_FULL_SCAN_RESULT = 2
252
253    SCAN_TYPE_LOW_LATENCY = 0
254    SCAN_TYPE_LOW_POWER = 1
255    SCAN_TYPE_HIGH_ACCURACY = 2
256
257    # US Wifi frequencies
258    ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
259                          2457, 2462]
260    DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560, 5580,
261                          5600, 5620, 5640, 5660, 5680, 5700, 5720]
262    NONE_DFS_5G_FREQUENCIES = [5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805,
263                               5825]
264    ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
265
266    band_to_frequencies = {
267        WIFI_BAND_24_GHZ: ALL_2G_FREQUENCIES,
268        WIFI_BAND_5_GHZ: NONE_DFS_5G_FREQUENCIES,
269        WIFI_BAND_5_GHZ_DFS_ONLY: DFS_5G_FREQUENCIES,
270        WIFI_BAND_5_GHZ_WITH_DFS: ALL_5G_FREQUENCIES,
271        WIFI_BAND_BOTH: ALL_2G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES,
272        WIFI_BAND_BOTH_WITH_DFS: ALL_5G_FREQUENCIES + ALL_2G_FREQUENCIES
273    }
274
275    # All Wifi frequencies to channels lookup.
276    freq_to_channel = {
277        2412: 1,
278        2417: 2,
279        2422: 3,
280        2427: 4,
281        2432: 5,
282        2437: 6,
283        2442: 7,
284        2447: 8,
285        2452: 9,
286        2457: 10,
287        2462: 11,
288        2467: 12,
289        2472: 13,
290        2484: 14,
291        4915: 183,
292        4920: 184,
293        4925: 185,
294        4935: 187,
295        4940: 188,
296        4945: 189,
297        4960: 192,
298        4980: 196,
299        5035: 7,
300        5040: 8,
301        5045: 9,
302        5055: 11,
303        5060: 12,
304        5080: 16,
305        5170: 34,
306        5180: 36,
307        5190: 38,
308        5200: 40,
309        5210: 42,
310        5220: 44,
311        5230: 46,
312        5240: 48,
313        5260: 52,
314        5280: 56,
315        5300: 60,
316        5320: 64,
317        5500: 100,
318        5520: 104,
319        5540: 108,
320        5560: 112,
321        5580: 116,
322        5600: 120,
323        5620: 124,
324        5640: 128,
325        5660: 132,
326        5680: 136,
327        5700: 140,
328        5745: 149,
329        5765: 153,
330        5785: 157,
331        5805: 161,
332        5825: 165,
333    }
334
335    # All Wifi channels to frequencies lookup.
336    channel_2G_to_freq = {
337        1: 2412,
338        2: 2417,
339        3: 2422,
340        4: 2427,
341        5: 2432,
342        6: 2437,
343        7: 2442,
344        8: 2447,
345        9: 2452,
346        10: 2457,
347        11: 2462,
348        12: 2467,
349        13: 2472,
350        14: 2484
351    }
352
353    channel_5G_to_freq = {
354        183: 4915,
355        184: 4920,
356        185: 4925,
357        187: 4935,
358        188: 4940,
359        189: 4945,
360        192: 4960,
361        196: 4980,
362        7: 5035,
363        8: 5040,
364        9: 5045,
365        11: 5055,
366        12: 5060,
367        16: 5080,
368        34: 5170,
369        36: 5180,
370        38: 5190,
371        40: 5200,
372        42: 5210,
373        44: 5220,
374        46: 5230,
375        48: 5240,
376        52: 5260,
377        56: 5280,
378        60: 5300,
379        64: 5320,
380        100: 5500,
381        104: 5520,
382        108: 5540,
383        112: 5560,
384        116: 5580,
385        120: 5600,
386        124: 5620,
387        128: 5640,
388        132: 5660,
389        136: 5680,
390        140: 5700,
391        149: 5745,
392        153: 5765,
393        157: 5785,
394        161: 5805,
395        165: 5825
396    }
397
398
399class WifiChannelBase:
400    ALL_2G_FREQUENCIES = []
401    DFS_5G_FREQUENCIES = []
402    NONE_DFS_5G_FREQUENCIES = []
403    ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
404    MIX_CHANNEL_SCAN = []
405
406    def band_to_freq(self, band):
407        _band_to_frequencies = {
408            WifiEnums.WIFI_BAND_24_GHZ: self.ALL_2G_FREQUENCIES,
409            WifiEnums.WIFI_BAND_5_GHZ: self.NONE_DFS_5G_FREQUENCIES,
410            WifiEnums.WIFI_BAND_5_GHZ_DFS_ONLY: self.DFS_5G_FREQUENCIES,
411            WifiEnums.WIFI_BAND_5_GHZ_WITH_DFS: self.ALL_5G_FREQUENCIES,
412            WifiEnums.WIFI_BAND_BOTH:
413            self.ALL_2G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES,
414            WifiEnums.WIFI_BAND_BOTH_WITH_DFS:
415            self.ALL_5G_FREQUENCIES + self.ALL_2G_FREQUENCIES
416        }
417        return _band_to_frequencies[band]
418
419
420class WifiChannelUS(WifiChannelBase):
421    # US Wifi frequencies
422    ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
423                          2457, 2462]
424    NONE_DFS_5G_FREQUENCIES = [5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805,
425                               5825]
426    MIX_CHANNEL_SCAN = [2412, 2437, 2462, 5180, 5200, 5280, 5260, 5300, 5500,
427                        5320, 5520, 5560, 5700, 5745, 5805]
428
429    def __init__(self, model=None):
430        self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520,
431                                   5540, 5560, 5580, 5600, 5620, 5640,
432                                   5660, 5680, 5700, 5720]
433        self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
434
435
436class WifiReferenceNetworks:
437    """ Class to parse and return networks of different band and
438        auth type from reference_networks
439    """
440    def __init__(self, obj):
441        self.reference_networks = obj
442        self.WIFI_2G = "2g"
443        self.WIFI_5G = "5g"
444
445        self.secure_networks_2g = []
446        self.secure_networks_5g = []
447        self.open_networks_2g = []
448        self.open_networks_5g = []
449        self._parse_networks()
450
451    def _parse_networks(self):
452        for network in self.reference_networks:
453            for key in network:
454                if key == self.WIFI_2G:
455                    if "password" in network[key]:
456                        self.secure_networks_2g.append(network[key])
457                    else:
458                        self.open_networks_2g.append(network[key])
459                else:
460                    if "password" in network[key]:
461                        self.secure_networks_5g.append(network[key])
462                    else:
463                        self.open_networks_5g.append(network[key])
464
465    def return_2g_secure_networks(self):
466        return self.secure_networks_2g
467
468    def return_5g_secure_networks(self):
469        return self.secure_networks_5g
470
471    def return_2g_open_networks(self):
472        return self.open_networks_2g
473
474    def return_5g_open_networks(self):
475        return self.open_networks_5g
476
477    def return_secure_networks(self):
478        return self.secure_networks_2g + self.secure_networks_5g
479
480    def return_open_networks(self):
481        return self.open_networks_2g + self.open_networks_5g
482
483
484def _assert_on_fail_handler(func, assert_on_fail, *args, **kwargs):
485    """Wrapper function that handles the bahevior of assert_on_fail.
486
487    When assert_on_fail is True, let all test signals through, which can
488    terminate test cases directly. When assert_on_fail is False, the wrapper
489    raises no test signals and reports operation status by returning True or
490    False.
491
492    Args:
493        func: The function to wrap. This function reports operation status by
494              raising test signals.
495        assert_on_fail: A boolean that specifies if the output of the wrapper
496                        is test signal based or return value based.
497        args: Positional args for func.
498        kwargs: Name args for func.
499
500    Returns:
501        If assert_on_fail is True, returns True/False to signal operation
502        status, otherwise return nothing.
503    """
504    try:
505        func(*args, **kwargs)
506        if not assert_on_fail:
507            return True
508    except signals.TestSignal:
509        if assert_on_fail:
510            raise
511        return False
512
513
514def assert_network_in_list(target, network_list):
515    """Makes sure a specified target Wi-Fi network exists in a list of Wi-Fi
516    networks.
517
518    Args:
519        target: A dict representing a Wi-Fi network.
520                E.g. {WifiEnums.SSID_KEY: "SomeNetwork"}
521        network_list: A list of dicts, each representing a Wi-Fi network.
522    """
523    match_results = match_networks(target, network_list)
524    asserts.assert_true(
525        match_results, "Target network %s, does not exist in network list %s" %
526        (target, network_list))
527
528
529def match_networks(target_params, networks):
530    """Finds the WiFi networks that match a given set of parameters in a list
531    of WiFi networks.
532
533    To be considered a match, the network should contain every key-value pair
534    of target_params
535
536    Args:
537        target_params: A dict with 1 or more key-value pairs representing a Wi-Fi network.
538                       E.g { 'SSID': 'wh_ap1_5g', 'BSSID': '30:b5:c2:33:e4:47' }
539        networks: A list of dict objects representing WiFi networks.
540
541    Returns:
542        The networks that match the target parameters.
543    """
544    results = []
545    asserts.assert_true(target_params,
546                        "Expected networks object 'target_params' is empty")
547    for n in networks:
548        add_network = 1
549        for k, v in target_params.items():
550            if k not in n:
551                add_network = 0
552                break
553            if n[k] != v:
554                add_network = 0
555                break
556        if add_network:
557            results.append(n)
558    return results
559
560
561def wait_for_wifi_state(ad, state, assert_on_fail=True):
562    """Waits for the device to transition to the specified wifi state
563
564    Args:
565        ad: An AndroidDevice object.
566        state: Wifi state to wait for.
567        assert_on_fail: If True, error checks in this function will raise test
568                        failure signals.
569
570    Returns:
571        If assert_on_fail is False, function returns True if the device transitions
572        to the specified state, False otherwise. If assert_on_fail is True, no return value.
573    """
574    return _assert_on_fail_handler(
575        _wait_for_wifi_state, assert_on_fail, ad, state=state)
576
577
578def _wait_for_wifi_state(ad, state):
579    """Toggles the state of wifi.
580
581    TestFailure signals are raised when something goes wrong.
582
583    Args:
584        ad: An AndroidDevice object.
585        state: Wifi state to wait for.
586    """
587    if state == ad.droid.wifiCheckState():
588        # Check if the state is already achieved, so we don't wait for the
589        # state change event by mistake.
590        return
591    ad.droid.wifiStartTrackingStateChange()
592    fail_msg = "Device did not transition to Wi-Fi state to %s on %s." % (state,
593                                                           ad.serial)
594    try:
595        ad.ed.wait_for_event(wifi_constants.WIFI_STATE_CHANGED,
596                             lambda x: x["data"]["enabled"] == state,
597                             SHORT_TIMEOUT)
598    except Empty:
599        asserts.assert_equal(state, ad.droid.wifiCheckState(), fail_msg)
600    finally:
601        ad.droid.wifiStopTrackingStateChange()
602
603
604def wifi_toggle_state(ad, new_state=None, assert_on_fail=True):
605    """Toggles the state of wifi.
606
607    Args:
608        ad: An AndroidDevice object.
609        new_state: Wifi state to set to. If None, opposite of the current state.
610        assert_on_fail: If True, error checks in this function will raise test
611                        failure signals.
612
613    Returns:
614        If assert_on_fail is False, function returns True if the toggle was
615        successful, False otherwise. If assert_on_fail is True, no return value.
616    """
617    return _assert_on_fail_handler(
618        _wifi_toggle_state, assert_on_fail, ad, new_state=new_state)
619
620
621def _wifi_toggle_state(ad, new_state=None):
622    """Toggles the state of wifi.
623
624    TestFailure signals are raised when something goes wrong.
625
626    Args:
627        ad: An AndroidDevice object.
628        new_state: The state to set Wi-Fi to. If None, opposite of the current
629                   state will be set.
630    """
631    if new_state is None:
632        new_state = not ad.droid.wifiCheckState()
633    elif new_state == ad.droid.wifiCheckState():
634        # Check if the new_state is already achieved, so we don't wait for the
635        # state change event by mistake.
636        return
637    ad.droid.wifiStartTrackingStateChange()
638    ad.log.info("Setting Wi-Fi state to %s.", new_state)
639    ad.ed.clear_all_events()
640    # Setting wifi state.
641    ad.droid.wifiToggleState(new_state)
642    fail_msg = "Failed to set Wi-Fi state to %s on %s." % (new_state,
643                                                           ad.serial)
644    try:
645        ad.ed.wait_for_event(wifi_constants.WIFI_STATE_CHANGED,
646                             lambda x: x["data"]["enabled"] == new_state,
647                             SHORT_TIMEOUT)
648    except Empty:
649        asserts.assert_equal(new_state, ad.droid.wifiCheckState(), fail_msg)
650    finally:
651        ad.droid.wifiStopTrackingStateChange()
652
653
654def reset_wifi(ad):
655    """Clears all saved Wi-Fi networks on a device.
656
657    This will turn Wi-Fi on.
658
659    Args:
660        ad: An AndroidDevice object.
661
662    """
663    networks = ad.droid.wifiGetConfiguredNetworks()
664    if not networks:
665        return
666    for n in networks:
667        ad.droid.wifiForgetNetwork(n['networkId'])
668        try:
669            event = ad.ed.pop_event(wifi_constants.WIFI_FORGET_NW_SUCCESS,
670                                    SHORT_TIMEOUT)
671        except Empty:
672            logging.warning("Could not confirm the removal of network %s.", n)
673    # Check again to see if there's any network left.
674    asserts.assert_true(
675        not ad.droid.wifiGetConfiguredNetworks(),
676        "Failed to remove these configured Wi-Fi networks: %s" % networks)
677
678
679def toggle_airplane_mode_on_and_off(ad):
680    """Turn ON and OFF Airplane mode.
681
682    ad: An AndroidDevice object.
683    Returns: Assert if turning on/off Airplane mode fails.
684
685    """
686    ad.log.debug("Toggling Airplane mode ON.")
687    asserts.assert_true(
688        utils.force_airplane_mode(ad, True),
689        "Can not turn on airplane mode on: %s" % ad.serial)
690    time.sleep(DEFAULT_TIMEOUT)
691    ad.log.debug("Toggling Airplane mode OFF.")
692    asserts.assert_true(
693        utils.force_airplane_mode(ad, False),
694        "Can not turn on airplane mode on: %s" % ad.serial)
695    time.sleep(DEFAULT_TIMEOUT)
696
697
698def toggle_wifi_off_and_on(ad):
699    """Turn OFF and ON WiFi.
700
701    ad: An AndroidDevice object.
702    Returns: Assert if turning off/on WiFi fails.
703
704    """
705    ad.log.debug("Toggling wifi OFF.")
706    wifi_toggle_state(ad, False)
707    time.sleep(DEFAULT_TIMEOUT)
708    ad.log.debug("Toggling wifi ON.")
709    wifi_toggle_state(ad, True)
710    time.sleep(DEFAULT_TIMEOUT)
711
712
713def wifi_forget_network(ad, net_ssid):
714    """Remove configured Wifi network on an android device.
715
716    Args:
717        ad: android_device object for forget network.
718        net_ssid: ssid of network to be forget
719
720    """
721    networks = ad.droid.wifiGetConfiguredNetworks()
722    if not networks:
723        return
724    for n in networks:
725        if net_ssid in n[WifiEnums.SSID_KEY]:
726            ad.droid.wifiForgetNetwork(n['networkId'])
727            try:
728                event = ad.ed.pop_event(wifi_constants.WIFI_FORGET_NW_SUCCESS,
729                                        SHORT_TIMEOUT)
730            except Empty:
731                asserts.fail("Failed to remove network %s." % n)
732
733
734def wifi_test_device_init(ad):
735    """Initializes an android device for wifi testing.
736
737    0. Make sure SL4A connection is established on the android device.
738    1. Disable location service's WiFi scan.
739    2. Turn WiFi on.
740    3. Clear all saved networks.
741    4. Set country code to US.
742    5. Enable WiFi verbose logging.
743    6. Sync device time with computer time.
744    7. Turn off cellular data.
745    8. Turn off ambient display.
746    """
747    utils.require_sl4a((ad, ))
748    ad.droid.wifiScannerToggleAlwaysAvailable(False)
749    msg = "Failed to turn off location service's scan."
750    asserts.assert_true(not ad.droid.wifiScannerIsAlwaysAvailable(), msg)
751    wifi_toggle_state(ad, True)
752    reset_wifi(ad)
753    ad.droid.wifiEnableVerboseLogging(1)
754    msg = "Failed to enable WiFi verbose logging."
755    asserts.assert_equal(ad.droid.wifiGetVerboseLoggingLevel(), 1, msg)
756    # We don't verify the following settings since they are not critical.
757    # Set wpa_supplicant log level to EXCESSIVE.
758    output = ad.adb.shell("wpa_cli -i wlan0 -p -g@android:wpa_wlan0 IFNAME="
759                          "wlan0 log_level EXCESSIVE")
760    ad.log.info("wpa_supplicant log change status: %s", output)
761    utils.sync_device_time(ad)
762    ad.droid.telephonyToggleDataConnection(False)
763    # TODO(angli): need to verify the country code was actually set. No generic
764    # way to check right now.
765    ad.adb.shell("halutil -country %s" % WifiEnums.CountryCode.US)
766    utils.set_ambient_display(ad, False)
767
768
769def start_wifi_connection_scan(ad):
770    """Starts a wifi connection scan and wait for results to become available.
771
772    Args:
773        ad: An AndroidDevice object.
774    """
775    ad.ed.clear_all_events()
776    ad.droid.wifiStartScan()
777    try:
778        ad.ed.pop_event("WifiManagerScanResultsAvailable", 60)
779    except Empty:
780        asserts.fail("Wi-Fi results did not become available within 60s.")
781
782
783def start_wifi_connection_scan_and_return_status(ad):
784    """
785    Starts a wifi connection scan and wait for results to become available
786    or a scan failure to be reported.
787
788    Args:
789        ad: An AndroidDevice object.
790    Returns:
791        True: if scan succeeded & results are available
792        False: if scan failed
793    """
794    ad.ed.clear_all_events()
795    ad.droid.wifiStartScan()
796    try:
797        events = ad.ed.pop_events(
798            "WifiManagerScan(ResultsAvailable|Failure)", 60)
799    except Empty:
800        asserts.fail(
801            "Wi-Fi scan results/failure did not become available within 60s.")
802    # If there are multiple matches, we check for atleast one success.
803    for event in events:
804        if event["name"] == "WifiManagerScanResultsAvailable":
805            return True
806        elif event["name"] == "WifiManagerScanFailure":
807            ad.log.debug("Scan failure received")
808    return False
809
810
811def start_wifi_connection_scan_and_check_for_network(ad, network_ssid,
812                                                     max_tries=3):
813    """
814    Start connectivity scans & checks if the |network_ssid| is seen in
815    scan results. The method performs a max of |max_tries| connectivity scans
816    to find the network.
817
818    Args:
819        ad: An AndroidDevice object.
820        network_ssid: SSID of the network we are looking for.
821        max_tries: Number of scans to try.
822    Returns:
823        True: if network_ssid is found in scan results.
824        False: if network_ssid is not found in scan results.
825    """
826    for num_tries in range(max_tries):
827        if start_wifi_connection_scan_and_return_status(ad):
828            scan_results = ad.droid.wifiGetScanResults()
829            match_results = match_networks(
830                {WifiEnums.SSID_KEY: network_ssid}, scan_results)
831            if len(match_results) > 0:
832                return True
833    return False
834
835
836def start_wifi_connection_scan_and_ensure_network_found(ad, network_ssid,
837                                                        max_tries=3):
838    """
839    Start connectivity scans & ensure the |network_ssid| is seen in
840    scan results. The method performs a max of |max_tries| connectivity scans
841    to find the network.
842    This method asserts on failure!
843
844    Args:
845        ad: An AndroidDevice object.
846        network_ssid: SSID of the network we are looking for.
847        max_tries: Number of scans to try.
848    """
849    ad.log.info("Starting scans to ensure %s is present", network_ssid)
850    assert_msg = "Failed to find " + network_ssid + " in scan results" \
851        " after " + str(max_tries) + " tries"
852    asserts.assert_true(start_wifi_connection_scan_and_check_for_network(
853        ad, network_ssid, max_tries), assert_msg)
854
855
856def start_wifi_connection_scan_and_ensure_network_not_found(ad, network_ssid,
857                                                            max_tries=3):
858    """
859    Start connectivity scans & ensure the |network_ssid| is not seen in
860    scan results. The method performs a max of |max_tries| connectivity scans
861    to find the network.
862    This method asserts on failure!
863
864    Args:
865        ad: An AndroidDevice object.
866        network_ssid: SSID of the network we are looking for.
867        max_tries: Number of scans to try.
868    """
869    ad.log.info("Starting scans to ensure %s is not present", network_ssid)
870    assert_msg = "Found " + network_ssid + " in scan results" \
871        " after " + str(max_tries) + " tries"
872    asserts.assert_false(start_wifi_connection_scan_and_check_for_network(
873        ad, network_ssid, max_tries), assert_msg)
874
875
876def start_wifi_background_scan(ad, scan_setting):
877    """Starts wifi background scan.
878
879    Args:
880        ad: android_device object to initiate connection on.
881        scan_setting: A dict representing the settings of the scan.
882
883    Returns:
884        If scan was started successfully, event data of success event is returned.
885    """
886    idx = ad.droid.wifiScannerStartBackgroundScan(scan_setting)
887    event = ad.ed.pop_event("WifiScannerScan{}onSuccess".format(idx),
888                            SHORT_TIMEOUT)
889    return event['data']
890
891
892def start_wifi_tethering(ad, ssid, password, band=None, hidden=None):
893    """Starts wifi tethering on an android_device.
894
895    Args:
896        ad: android_device to start wifi tethering on.
897        ssid: The SSID the soft AP should broadcast.
898        password: The password the soft AP should use.
899        band: The band the soft AP should be set on. It should be either
900            WifiEnums.WIFI_CONFIG_APBAND_2G or WifiEnums.WIFI_CONFIG_APBAND_5G.
901        hidden: boolean to indicate if the AP needs to be hidden or not.
902
903    Returns:
904        No return value. Error checks in this function will raise test failure signals
905    """
906    config = {WifiEnums.SSID_KEY: ssid}
907    if password:
908        config[WifiEnums.PWD_KEY] = password
909    if band:
910        config[WifiEnums.APBAND_KEY] = band
911    if hidden:
912      config[WifiEnums.HIDDEN_KEY] = hidden
913    asserts.assert_true(
914        ad.droid.wifiSetWifiApConfiguration(config),
915        "Failed to update WifiAp Configuration")
916    ad.droid.wifiStartTrackingTetherStateChange()
917    ad.droid.connectivityStartTethering(tel_defines.TETHERING_WIFI, False)
918    try:
919        ad.ed.pop_event("ConnectivityManagerOnTetheringStarted")
920        ad.ed.wait_for_event("TetherStateChanged",
921                             lambda x: x["data"]["ACTIVE_TETHER"], 30)
922        ad.log.debug("Tethering started successfully.")
923    except Empty:
924        msg = "Failed to receive confirmation of wifi tethering starting"
925        asserts.fail(msg)
926    finally:
927        ad.droid.wifiStopTrackingTetherStateChange()
928
929
930def save_wifi_soft_ap_config(ad, wifi_config, band=None, hidden=None):
931    """ Save a soft ap configuration """
932    if band:
933        wifi_config[WifiEnums.APBAND_KEY] = band
934    if hidden:
935        wifi_config[WifiEnums.HIDDEN_KEY] = hidden
936    asserts.assert_true(ad.droid.wifiSetWifiApConfiguration(wifi_config),
937                        "Failed to set WifiAp Configuration")
938
939    wifi_ap = ad.droid.wifiGetApConfiguration()
940    asserts.assert_true(
941        wifi_ap[WifiEnums.SSID_KEY] == wifi_config[WifiEnums.SSID_KEY],
942        "Hotspot SSID doesn't match with expected SSID")
943
944
945def start_wifi_tethering_saved_config(ad):
946    """ Turn on wifi hotspot with a config that is already saved """
947    ad.droid.wifiStartTrackingTetherStateChange()
948    ad.droid.connectivityStartTethering(tel_defines.TETHERING_WIFI, False)
949    try:
950        ad.ed.pop_event("ConnectivityManagerOnTetheringStarted")
951        ad.ed.wait_for_event("TetherStateChanged",
952                             lambda x: x["data"]["ACTIVE_TETHER"], 30)
953    except:
954        asserts.fail("Didn't receive wifi tethering starting confirmation")
955    finally:
956        ad.droid.wifiStopTrackingTetherStateChange()
957
958
959def stop_wifi_tethering(ad):
960    """Stops wifi tethering on an android_device.
961
962    Args:
963        ad: android_device to stop wifi tethering on.
964    """
965    ad.droid.wifiStartTrackingTetherStateChange()
966    ad.droid.connectivityStopTethering(tel_defines.TETHERING_WIFI)
967    try:
968        ad.ed.pop_event("WifiManagerApDisabled", 30)
969        ad.ed.wait_for_event("TetherStateChanged",
970                             lambda x: not x["data"]["ACTIVE_TETHER"], 30)
971    except Empty:
972        msg = "Failed to receive confirmation of wifi tethering stopping"
973        asserts.fail(msg)
974    finally:
975        ad.droid.wifiStopTrackingTetherStateChange()
976
977
978def toggle_wifi_and_wait_for_reconnection(ad,
979                                          network,
980                                          num_of_tries=1,
981                                          assert_on_fail=True):
982    """Toggle wifi state and then wait for Android device to reconnect to
983    the provided wifi network.
984
985    This expects the device to be already connected to the provided network.
986
987    Logic steps are
988     1. Ensure that we're connected to the network.
989     2. Turn wifi off.
990     3. Wait for 10 seconds.
991     4. Turn wifi on.
992     5. Wait for the "connected" event, then confirm the connected ssid is the
993        one requested.
994
995    Args:
996        ad: android_device object to initiate connection on.
997        network: A dictionary representing the network to await connection. The
998                 dictionary must have the key "SSID".
999        num_of_tries: An integer that is the number of times to try before
1000                      delaring failure. Default is 1.
1001        assert_on_fail: If True, error checks in this function will raise test
1002                        failure signals.
1003
1004    Returns:
1005        If assert_on_fail is False, function returns True if the toggle was
1006        successful, False otherwise. If assert_on_fail is True, no return value.
1007    """
1008    return _assert_on_fail_handler(
1009        _toggle_wifi_and_wait_for_reconnection,
1010        assert_on_fail,
1011        ad,
1012        network,
1013        num_of_tries=num_of_tries)
1014
1015
1016def _toggle_wifi_and_wait_for_reconnection(ad, network, num_of_tries=1):
1017    """Toggle wifi state and then wait for Android device to reconnect to
1018    the provided wifi network.
1019
1020    This expects the device to be already connected to the provided network.
1021
1022    Logic steps are
1023     1. Ensure that we're connected to the network.
1024     2. Turn wifi off.
1025     3. Wait for 10 seconds.
1026     4. Turn wifi on.
1027     5. Wait for the "connected" event, then confirm the connected ssid is the
1028        one requested.
1029
1030    This will directly fail a test if anything goes wrong.
1031
1032    Args:
1033        ad: android_device object to initiate connection on.
1034        network: A dictionary representing the network to await connection. The
1035                 dictionary must have the key "SSID".
1036        num_of_tries: An integer that is the number of times to try before
1037                      delaring failure. Default is 1.
1038    """
1039    expected_ssid = network[WifiEnums.SSID_KEY]
1040    # First ensure that we're already connected to the provided network.
1041    verify_con = {WifiEnums.SSID_KEY: expected_ssid}
1042    verify_wifi_connection_info(ad, verify_con)
1043    # Now toggle wifi state and wait for the connection event.
1044    wifi_toggle_state(ad, False)
1045    time.sleep(10)
1046    wifi_toggle_state(ad, True)
1047    ad.droid.wifiStartTrackingStateChange()
1048    try:
1049        connect_result = None
1050        for i in range(num_of_tries):
1051            try:
1052                connect_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED,
1053                                                 30)
1054                break
1055            except Empty:
1056                pass
1057        asserts.assert_true(connect_result,
1058                            "Failed to connect to Wi-Fi network %s on %s" %
1059                            (network, ad.serial))
1060        logging.debug("Connection result on %s: %s.", ad.serial,
1061                      connect_result)
1062        actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
1063        asserts.assert_equal(actual_ssid, expected_ssid,
1064                             "Connected to the wrong network on %s."
1065                             "Expected %s, but got %s." %
1066                             (ad.serial, expected_ssid, actual_ssid))
1067        logging.info("Connected to Wi-Fi network %s on %s", actual_ssid,
1068                     ad.serial)
1069    finally:
1070        ad.droid.wifiStopTrackingStateChange()
1071
1072
1073def wait_for_connect(ad, expected_ssid=None, expected_id=None, tries=2,
1074                     assert_on_fail=True):
1075    """Wait for a connect event.
1076
1077    This will directly fail a test if anything goes wrong.
1078
1079    Args:
1080        ad: An Android device object.
1081        expected_ssid: SSID of the network to connect to.
1082        expected_id: Network Id of the network to connect to.
1083        tries: An integer that is the number of times to try before failing.
1084        assert_on_fail: If True, error checks in this function will raise test
1085                        failure signals.
1086
1087    Returns:
1088        Returns a value only if assert_on_fail is false.
1089        Returns True if the connection was successful, False otherwise.
1090    """
1091    return _assert_on_fail_handler(
1092        _wait_for_connect, assert_on_fail, ad, expected_ssid, expected_id,
1093        tries)
1094
1095
1096def _wait_for_connect(ad, expected_ssid=None, expected_id=None, tries=2):
1097    """Wait for a connect event.
1098
1099    Args:
1100        ad: An Android device object.
1101        expected_ssid: SSID of the network to connect to.
1102        expected_id: Network Id of the network to connect to.
1103        tries: An integer that is the number of times to try before failing.
1104    """
1105    ad.droid.wifiStartTrackingStateChange()
1106    try:
1107        connect_result = _wait_for_connect_event(
1108            ad, ssid=expected_ssid, id=expected_id, tries=tries)
1109        asserts.assert_true(connect_result,
1110                            "Failed to connect to Wi-Fi network %s" %
1111                            expected_ssid)
1112        ad.log.debug("Wi-Fi connection result: %s.", connect_result)
1113        actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
1114        if expected_ssid:
1115            asserts.assert_equal(actual_ssid, expected_ssid,
1116                                 "Connected to the wrong network")
1117        actual_id = connect_result['data'][WifiEnums.NETID_KEY]
1118        if expected_id:
1119            asserts.assert_equal(actual_id, expected_id,
1120                                 "Connected to the wrong network")
1121        ad.log.info("Connected to Wi-Fi network %s.", actual_ssid)
1122    except Empty:
1123        asserts.fail("Failed to start connection process to %s" %
1124                     expected_ssid)
1125    except Exception as error:
1126        ad.log.error("Failed to connect to %s with error %s", expected_ssid,
1127                     error)
1128        raise signals.TestFailure("Failed to connect to %s network" %
1129                                  expected_ssid)
1130    finally:
1131        ad.droid.wifiStopTrackingStateChange()
1132
1133
1134def _wait_for_connect_event(ad, ssid=None, id=None, tries=1):
1135    """Wait for a connect event on queue and pop when available.
1136
1137    Args:
1138        ad: An Android device object.
1139        ssid: SSID of the network to connect to.
1140        id: Network Id of the network to connect to.
1141        tries: An integer that is the number of times to try before failing.
1142
1143    Returns:
1144        A dict with details of the connection data, which looks like this:
1145        {
1146         'time': 1485460337798,
1147         'name': 'WifiNetworkConnected',
1148         'data': {
1149                  'rssi': -27,
1150                  'is_24ghz': True,
1151                  'mac_address': '02:00:00:00:00:00',
1152                  'network_id': 1,
1153                  'BSSID': '30:b5:c2:33:d3:fc',
1154                  'ip_address': 117483712,
1155                  'link_speed': 54,
1156                  'supplicant_state': 'completed',
1157                  'hidden_ssid': False,
1158                  'SSID': 'wh_ap1_2g',
1159                  'is_5ghz': False}
1160        }
1161
1162    """
1163    conn_result = None
1164
1165    # If ssid and network id is None, just wait for any connect event.
1166    if id is None and ssid is None:
1167        for i in range(tries):
1168            try:
1169                start = time.time()
1170                conn_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED, 30)
1171                _assert_connection_time(start)
1172                break
1173            except Empty:
1174                pass
1175    else:
1176    # If ssid or network id is specified, wait for specific connect event.
1177        for i in range(tries):
1178            try:
1179                start = time.time()
1180                conn_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED, 30)
1181                if id and conn_result['data'][WifiEnums.NETID_KEY] == id:
1182                    _assert_connection_time(start)
1183                    break
1184                elif ssid and conn_result['data'][WifiEnums.SSID_KEY] == ssid:
1185                    _assert_connection_time(start)
1186                    break
1187            except Empty:
1188                pass
1189
1190    return conn_result
1191
1192def _assert_connection_time(start):
1193    duration = time.time() - start
1194    asserts.assert_true(
1195        duration < WIFI_ABNORMAL_CONNECTION_TIME,
1196        "Took " + str(duration) + "s to connect to network, " +
1197        " expected " + str(WIFI_ABNORMAL_CONNECTION_TIME))
1198
1199def wait_for_disconnect(ad, timeout=10):
1200    """Wait for a disconnect event within the specified timeout.
1201
1202    Args:
1203        ad: Android device object.
1204        timeout: Timeout in seconds.
1205
1206    """
1207    try:
1208        ad.droid.wifiStartTrackingStateChange()
1209        event = ad.ed.pop_event("WifiNetworkDisconnected", timeout)
1210    except Empty:
1211        raise signals.TestFailure("Device did not disconnect from the network")
1212    finally:
1213        ad.droid.wifiStopTrackingStateChange()
1214
1215
1216def ensure_no_disconnect(ad, duration=10):
1217    """Ensure that there is no disconnect for the specified duration.
1218
1219    Args:
1220        ad: Android device object.
1221        duration: Duration in seconds.
1222
1223    """
1224    try:
1225        ad.droid.wifiStartTrackingStateChange()
1226        event = ad.ed.pop_event("WifiNetworkDisconnected", duration)
1227        raise signals.TestFailure("Device disconnected from the network")
1228    except Empty:
1229        pass
1230    finally:
1231        ad.droid.wifiStopTrackingStateChange()
1232
1233
1234def connect_to_wifi_network(ad, network, assert_on_fail=True,
1235        check_connectivity=True):
1236    """Connection logic for open and psk wifi networks.
1237
1238    Args:
1239        ad: AndroidDevice to use for connection
1240        network: network info of the network to connect to
1241        assert_on_fail: If true, errors from wifi_connect will raise
1242                        test failure signals.
1243    """
1244    start_wifi_connection_scan_and_ensure_network_found(
1245        ad, network[WifiEnums.SSID_KEY])
1246    wifi_connect(ad,
1247                 network,
1248                 num_of_tries=3,
1249                 assert_on_fail=assert_on_fail,
1250                 check_connectivity=check_connectivity)
1251
1252
1253def connect_to_wifi_network_with_id(ad, network_id, network_ssid):
1254    """Connect to the given network using network id and verify SSID.
1255
1256    Args:
1257        network_id: int Network Id of the network.
1258        network_ssid: string SSID of the network.
1259
1260    Returns: True if connect using network id was successful;
1261             False otherwise.
1262
1263    """
1264    start_wifi_connection_scan_and_ensure_network_found(ad, network_ssid)
1265    wifi_connect_by_id(ad, network_id)
1266    connect_data = ad.droid.wifiGetConnectionInfo()
1267    connect_ssid = connect_data[WifiEnums.SSID_KEY]
1268    ad.log.debug("Expected SSID = %s Connected SSID = %s" %
1269                   (network_ssid, connect_ssid))
1270    if connect_ssid != network_ssid:
1271        return False
1272    return True
1273
1274
1275def wifi_connect(ad, network, num_of_tries=1, assert_on_fail=True,
1276        check_connectivity=True):
1277    """Connect an Android device to a wifi network.
1278
1279    Initiate connection to a wifi network, wait for the "connected" event, then
1280    confirm the connected ssid is the one requested.
1281
1282    This will directly fail a test if anything goes wrong.
1283
1284    Args:
1285        ad: android_device object to initiate connection on.
1286        network: A dictionary representing the network to connect to. The
1287                 dictionary must have the key "SSID".
1288        num_of_tries: An integer that is the number of times to try before
1289                      delaring failure. Default is 1.
1290        assert_on_fail: If True, error checks in this function will raise test
1291                        failure signals.
1292
1293    Returns:
1294        Returns a value only if assert_on_fail is false.
1295        Returns True if the connection was successful, False otherwise.
1296    """
1297    return _assert_on_fail_handler(
1298        _wifi_connect, assert_on_fail, ad, network, num_of_tries=num_of_tries,
1299          check_connectivity=check_connectivity)
1300
1301
1302def _wifi_connect(ad, network, num_of_tries=1, check_connectivity=True):
1303    """Connect an Android device to a wifi network.
1304
1305    Initiate connection to a wifi network, wait for the "connected" event, then
1306    confirm the connected ssid is the one requested.
1307
1308    This will directly fail a test if anything goes wrong.
1309
1310    Args:
1311        ad: android_device object to initiate connection on.
1312        network: A dictionary representing the network to connect to. The
1313                 dictionary must have the key "SSID".
1314        num_of_tries: An integer that is the number of times to try before
1315                      delaring failure. Default is 1.
1316    """
1317    asserts.assert_true(WifiEnums.SSID_KEY in network,
1318                        "Key '%s' must be present in network definition." %
1319                        WifiEnums.SSID_KEY)
1320    ad.droid.wifiStartTrackingStateChange()
1321    expected_ssid = network[WifiEnums.SSID_KEY]
1322    ad.droid.wifiConnectByConfig(network)
1323    ad.log.info("Starting connection process to %s", expected_ssid)
1324    try:
1325        event = ad.ed.pop_event(wifi_constants.CONNECT_BY_CONFIG_SUCCESS, 30)
1326        connect_result = _wait_for_connect_event(
1327            ad, ssid=expected_ssid, tries=num_of_tries)
1328        asserts.assert_true(connect_result,
1329                            "Failed to connect to Wi-Fi network %s on %s" %
1330                            (network, ad.serial))
1331        ad.log.debug("Wi-Fi connection result: %s.", connect_result)
1332        actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
1333        asserts.assert_equal(actual_ssid, expected_ssid,
1334                             "Connected to the wrong network on %s." %
1335                             ad.serial)
1336        ad.log.info("Connected to Wi-Fi network %s.", actual_ssid)
1337
1338        # Wait for data connection to stabilize.
1339        time.sleep(5)
1340
1341        if check_connectivity:
1342            internet = validate_connection(ad, DEFAULT_PING_ADDR)
1343            if not internet:
1344                raise signals.TestFailure("Failed to connect to internet on %s" %
1345                                          expected_ssid)
1346    except Empty:
1347        asserts.fail("Failed to start connection process to %s on %s" %
1348                     (network, ad.serial))
1349    except Exception as error:
1350        ad.log.error("Failed to connect to %s with error %s", expected_ssid,
1351                     error)
1352        raise signals.TestFailure("Failed to connect to %s network" % network)
1353
1354    finally:
1355        ad.droid.wifiStopTrackingStateChange()
1356
1357
1358def wifi_connect_by_id(ad, network_id, num_of_tries=3, assert_on_fail=True):
1359    """Connect an Android device to a wifi network using network Id.
1360
1361    Start connection to the wifi network, with the given network Id, wait for
1362    the "connected" event, then verify the connected network is the one requested.
1363
1364    This will directly fail a test if anything goes wrong.
1365
1366    Args:
1367        ad: android_device object to initiate connection on.
1368        network_id: Integer specifying the network id of the network.
1369        num_of_tries: An integer that is the number of times to try before
1370                      delaring failure. Default is 1.
1371        assert_on_fail: If True, error checks in this function will raise test
1372                        failure signals.
1373
1374    Returns:
1375        Returns a value only if assert_on_fail is false.
1376        Returns True if the connection was successful, False otherwise.
1377    """
1378    _assert_on_fail_handler(_wifi_connect_by_id, assert_on_fail, ad,
1379                            network_id, num_of_tries)
1380
1381
1382def _wifi_connect_by_id(ad, network_id, num_of_tries=1):
1383    """Connect an Android device to a wifi network using it's network id.
1384
1385    Start connection to the wifi network, with the given network id, wait for
1386    the "connected" event, then verify the connected network is the one requested.
1387
1388    Args:
1389        ad: android_device object to initiate connection on.
1390        network_id: Integer specifying the network id of the network.
1391        num_of_tries: An integer that is the number of times to try before
1392                      delaring failure. Default is 1.
1393    """
1394    ad.droid.wifiStartTrackingStateChange()
1395    # Clear all previous events.
1396    ad.ed.clear_all_events()
1397    ad.droid.wifiConnectByNetworkId(network_id)
1398    ad.log.info("Starting connection to network with id %d", network_id)
1399    try:
1400        event = ad.ed.pop_event(wifi_constants.CONNECT_BY_NETID_SUCCESS, 60)
1401        connect_result = _wait_for_connect_event(
1402            ad, id=network_id, tries=num_of_tries)
1403        asserts.assert_true(connect_result,
1404                            "Failed to connect to Wi-Fi network using network id")
1405        ad.log.debug("Wi-Fi connection result: %s", connect_result)
1406        actual_id = connect_result['data'][WifiEnums.NETID_KEY]
1407        asserts.assert_equal(actual_id, network_id,
1408                             "Connected to the wrong network on %s."
1409                             "Expected network id = %d, but got %d." %
1410                             (ad.serial, network_id, actual_id))
1411        expected_ssid = connect_result['data'][WifiEnums.SSID_KEY]
1412        ad.log.info("Connected to Wi-Fi network %s with %d network id.",
1413                     expected_ssid, network_id)
1414
1415        # Wait for data connection to stabilize.
1416        time.sleep(5)
1417
1418        internet = validate_connection(ad, DEFAULT_PING_ADDR)
1419        if not internet:
1420            raise signals.TestFailure("Failed to connect to internet on %s" %
1421                                      expected_ssid)
1422    except Empty:
1423        asserts.fail("Failed to connect to network with id %d on %s" %
1424                    (network_id, ad.serial))
1425    except Exception as error:
1426        ad.log.error("Failed to connect to network with id %d with error %s",
1427                      network_id, error)
1428        raise signals.TestFailure("Failed to connect to network with network"
1429                                  " id %d" % network_id)
1430    finally:
1431        ad.droid.wifiStopTrackingStateChange()
1432
1433def wifi_connect_using_network_request(ad, network, network_specifier,
1434                                       num_of_tries=3, assert_on_fail=True):
1435    """Connect an Android device to a wifi network using network request.
1436
1437    Trigger a network request with the provided network specifier,
1438    wait for the "onMatch" event, ensure that the scan results in "onMatch"
1439    event contain the specified network, then simulate the user granting the
1440    request with the specified network selected. Then wait for the "onAvailable"
1441    network callback indicating successful connection to network.
1442
1443    This will directly fail a test if anything goes wrong.
1444
1445    Args:
1446        ad: android_device object to initiate connection on.
1447        network_specifier: A dictionary representing the network specifier to
1448                           use.
1449        network: A dictionary representing the network to connect to. The
1450                 dictionary must have the key "SSID".
1451        num_of_tries: An integer that is the number of times to try before
1452                      delaring failure.
1453        assert_on_fail: If True, error checks in this function will raise test
1454                        failure signals.
1455
1456    Returns:
1457        Returns a value only if assert_on_fail is false.
1458        Returns True if the connection was successful, False otherwise.
1459    """
1460    _assert_on_fail_handler(_wifi_connect_using_network_request, assert_on_fail,
1461                            ad, network, network_specifier, num_of_tries)
1462
1463
1464def _wifi_connect_using_network_request(ad, network, network_specifier,
1465                                        num_of_tries=3):
1466    """Connect an Android device to a wifi network using network request.
1467
1468    Trigger a network request with the provided network specifier,
1469    wait for the "onMatch" event, ensure that the scan results in "onMatch"
1470    event contain the specified network, then simulate the user granting the
1471    request with the specified network selected. Then wait for the "onAvailable"
1472    network callback indicating successful connection to network.
1473
1474    Args:
1475        ad: android_device object to initiate connection on.
1476        network_specifier: A dictionary representing the network specifier to
1477                           use.
1478        network: A dictionary representing the network to connect to. The
1479                 dictionary must have the key "SSID".
1480        num_of_tries: An integer that is the number of times to try before
1481                      delaring failure.
1482    """
1483    ad.droid.wifiRequestNetworkWithSpecifier(network_specifier)
1484    ad.log.info("Sent network request with %s", network_specifier)
1485    # Need a delay here because UI interaction should only start once wifi
1486    # starts processing the request.
1487    time.sleep(wifi_constants.NETWORK_REQUEST_CB_REGISTER_DELAY_SEC)
1488    _wait_for_wifi_connect_after_network_request(ad, network, num_of_tries)
1489
1490
1491def wait_for_wifi_connect_after_network_request(ad, network, num_of_tries=3,
1492                                                assert_on_fail=True):
1493    """
1494    Simulate and verify the connection flow after initiating the network
1495    request.
1496
1497    Wait for the "onMatch" event, ensure that the scan results in "onMatch"
1498    event contain the specified network, then simulate the user granting the
1499    request with the specified network selected. Then wait for the "onAvailable"
1500    network callback indicating successful connection to network.
1501
1502    Args:
1503        ad: android_device object to initiate connection on.
1504        network: A dictionary representing the network to connect to. The
1505                 dictionary must have the key "SSID".
1506        num_of_tries: An integer that is the number of times to try before
1507                      delaring failure.
1508        assert_on_fail: If True, error checks in this function will raise test
1509                        failure signals.
1510
1511    Returns:
1512        Returns a value only if assert_on_fail is false.
1513        Returns True if the connection was successful, False otherwise.
1514    """
1515    _assert_on_fail_handler(_wait_for_wifi_connect_after_network_request,
1516                            assert_on_fail, ad, network, num_of_tries)
1517
1518
1519def _wait_for_wifi_connect_after_network_request(ad, network, num_of_tries=3):
1520    """
1521    Simulate and verify the connection flow after initiating the network
1522    request.
1523
1524    Wait for the "onMatch" event, ensure that the scan results in "onMatch"
1525    event contain the specified network, then simulate the user granting the
1526    request with the specified network selected. Then wait for the "onAvailable"
1527    network callback indicating successful connection to network.
1528
1529    Args:
1530        ad: android_device object to initiate connection on.
1531        network: A dictionary representing the network to connect to. The
1532                 dictionary must have the key "SSID".
1533        num_of_tries: An integer that is the number of times to try before
1534                      delaring failure.
1535    """
1536    asserts.assert_true(WifiEnums.SSID_KEY in network,
1537                        "Key '%s' must be present in network definition." %
1538                        WifiEnums.SSID_KEY)
1539    ad.droid.wifiStartTrackingStateChange()
1540    expected_ssid = network[WifiEnums.SSID_KEY]
1541    ad.droid.wifiRegisterNetworkRequestMatchCallback()
1542    # Wait for the platform to scan and return a list of networks
1543    # matching the request
1544    try:
1545        matched_network = None
1546        for _ in [0,  num_of_tries]:
1547            on_match_event = ad.ed.pop_event(
1548                wifi_constants.WIFI_NETWORK_REQUEST_MATCH_CB_ON_MATCH, 60)
1549            asserts.assert_true(on_match_event,
1550                                "Network request on match not received.")
1551            matched_scan_results = on_match_event["data"]
1552            ad.log.debug("Network request on match results %s",
1553                         matched_scan_results)
1554            matched_network = match_networks(
1555                {WifiEnums.SSID_KEY: network[WifiEnums.SSID_KEY]},
1556                matched_scan_results)
1557            if matched_network:
1558                break;
1559
1560        asserts.assert_true(
1561            matched_network, "Target network %s not found" % network)
1562
1563        ad.droid.wifiSendUserSelectionForNetworkRequestMatch(network)
1564        ad.log.info("Sent user selection for network request %s",
1565                    expected_ssid)
1566
1567        # Wait for the platform to connect to the network.
1568        on_available_event = ad.ed.pop_event(
1569            wifi_constants.WIFI_NETWORK_CB_ON_AVAILABLE, 60)
1570        asserts.assert_true(on_available_event,
1571                            "Network request on available not received.")
1572        connected_network = on_available_event["data"]
1573        ad.log.info("Connected to network %s", connected_network)
1574        asserts.assert_equal(connected_network[WifiEnums.SSID_KEY],
1575                             expected_ssid,
1576                             "Connected to the wrong network."
1577                             "Expected %s, but got %s." %
1578                             (network, connected_network))
1579    except Empty:
1580        asserts.fail("Failed to connect to %s" % expected_ssid)
1581    except Exception as error:
1582        ad.log.error("Failed to connect to %s with error %s",
1583                     (expected_ssid, error))
1584        raise signals.TestFailure("Failed to connect to %s network" % network)
1585    finally:
1586        ad.droid.wifiStopTrackingStateChange()
1587
1588
1589def wifi_passpoint_connect(ad, passpoint_network, num_of_tries=1,
1590                           assert_on_fail=True):
1591    """Connect an Android device to a wifi network.
1592
1593    Initiate connection to a wifi network, wait for the "connected" event, then
1594    confirm the connected ssid is the one requested.
1595
1596    This will directly fail a test if anything goes wrong.
1597
1598    Args:
1599        ad: android_device object to initiate connection on.
1600        passpoint_network: SSID of the Passpoint network to connect to.
1601        num_of_tries: An integer that is the number of times to try before
1602                      delaring failure. Default is 1.
1603        assert_on_fail: If True, error checks in this function will raise test
1604                        failure signals.
1605
1606    Returns:
1607        If assert_on_fail is False, function returns network id, if the connect was
1608        successful, False otherwise. If assert_on_fail is True, no return value.
1609    """
1610    _assert_on_fail_handler(_wifi_passpoint_connect, assert_on_fail, ad,
1611                            passpoint_network, num_of_tries = num_of_tries)
1612
1613
1614def _wifi_passpoint_connect(ad, passpoint_network, num_of_tries=1):
1615    """Connect an Android device to a wifi network.
1616
1617    Initiate connection to a wifi network, wait for the "connected" event, then
1618    confirm the connected ssid is the one requested.
1619
1620    This will directly fail a test if anything goes wrong.
1621
1622    Args:
1623        ad: android_device object to initiate connection on.
1624        passpoint_network: SSID of the Passpoint network to connect to.
1625        num_of_tries: An integer that is the number of times to try before
1626                      delaring failure. Default is 1.
1627    """
1628    ad.droid.wifiStartTrackingStateChange()
1629    expected_ssid = passpoint_network
1630    ad.log.info("Starting connection process to passpoint %s", expected_ssid)
1631
1632    try:
1633        connect_result = _wait_for_connect_event(
1634            ad, expected_ssid, num_of_tries)
1635        asserts.assert_true(connect_result,
1636                            "Failed to connect to WiFi passpoint network %s on"
1637                            " %s" % (expected_ssid, ad.serial))
1638        ad.log.info("Wi-Fi connection result: %s.", connect_result)
1639        actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
1640        asserts.assert_equal(actual_ssid, expected_ssid,
1641                             "Connected to the wrong network on %s." % ad.serial)
1642        ad.log.info("Connected to Wi-Fi passpoint network %s.", actual_ssid)
1643
1644        # Wait for data connection to stabilize.
1645        time.sleep(5)
1646
1647        internet = validate_connection(ad, DEFAULT_PING_ADDR)
1648        if not internet:
1649            raise signals.TestFailure("Failed to connect to internet on %s" %
1650                                      expected_ssid)
1651    except Exception as error:
1652        ad.log.error("Failed to connect to passpoint network %s with error %s",
1653                      expected_ssid, error)
1654        raise signals.TestFailure("Failed to connect to %s passpoint network" %
1655                                   expected_ssid)
1656
1657    finally:
1658        ad.droid.wifiStopTrackingStateChange()
1659
1660
1661def delete_passpoint(ad, fqdn):
1662    """Delete a required Passpoint configuration."""
1663    try:
1664        ad.droid.removePasspointConfig(fqdn)
1665        return True
1666    except Exception as error:
1667        ad.log.error("Failed to remove passpoint configuration with FQDN=%s "
1668                     "and error=%s" , fqdn, error)
1669        return False
1670
1671
1672def start_wifi_single_scan(ad, scan_setting):
1673    """Starts wifi single shot scan.
1674
1675    Args:
1676        ad: android_device object to initiate connection on.
1677        scan_setting: A dict representing the settings of the scan.
1678
1679    Returns:
1680        If scan was started successfully, event data of success event is returned.
1681    """
1682    idx = ad.droid.wifiScannerStartScan(scan_setting)
1683    event = ad.ed.pop_event("WifiScannerScan%sonSuccess" % idx, SHORT_TIMEOUT)
1684    ad.log.debug("Got event %s", event)
1685    return event['data']
1686
1687
1688def track_connection(ad, network_ssid, check_connection_count):
1689    """Track wifi connection to network changes for given number of counts
1690
1691    Args:
1692        ad: android_device object for forget network.
1693        network_ssid: network ssid to which connection would be tracked
1694        check_connection_count: Integer for maximum number network connection
1695                                check.
1696    Returns:
1697        True if connection to given network happen, else return False.
1698    """
1699    ad.droid.wifiStartTrackingStateChange()
1700    while check_connection_count > 0:
1701        connect_network = ad.ed.pop_event("WifiNetworkConnected", 120)
1702        ad.log.info("Connected to network %s", connect_network)
1703        if (WifiEnums.SSID_KEY in connect_network['data'] and
1704                connect_network['data'][WifiEnums.SSID_KEY] == network_ssid):
1705            return True
1706        check_connection_count -= 1
1707    ad.droid.wifiStopTrackingStateChange()
1708    return False
1709
1710
1711def get_scan_time_and_channels(wifi_chs, scan_setting, stime_channel):
1712    """Calculate the scan time required based on the band or channels in scan
1713    setting
1714
1715    Args:
1716        wifi_chs: Object of channels supported
1717        scan_setting: scan setting used for start scan
1718        stime_channel: scan time per channel
1719
1720    Returns:
1721        scan_time: time required for completing a scan
1722        scan_channels: channel used for scanning
1723    """
1724    scan_time = 0
1725    scan_channels = []
1726    if "band" in scan_setting and "channels" not in scan_setting:
1727        scan_channels = wifi_chs.band_to_freq(scan_setting["band"])
1728    elif "channels" in scan_setting and "band" not in scan_setting:
1729        scan_channels = scan_setting["channels"]
1730    scan_time = len(scan_channels) * stime_channel
1731    for channel in scan_channels:
1732        if channel in WifiEnums.DFS_5G_FREQUENCIES:
1733            scan_time += 132  #passive scan time on DFS
1734    return scan_time, scan_channels
1735
1736
1737def start_wifi_track_bssid(ad, track_setting):
1738    """Start tracking Bssid for the given settings.
1739
1740    Args:
1741      ad: android_device object.
1742      track_setting: Setting for which the bssid tracking should be started
1743
1744    Returns:
1745      If tracking started successfully, event data of success event is returned.
1746    """
1747    idx = ad.droid.wifiScannerStartTrackingBssids(
1748        track_setting["bssidInfos"], track_setting["apLostThreshold"])
1749    event = ad.ed.pop_event("WifiScannerBssid{}onSuccess".format(idx),
1750                            SHORT_TIMEOUT)
1751    return event['data']
1752
1753
1754def convert_pem_key_to_pkcs8(in_file, out_file):
1755    """Converts the key file generated by us to the format required by
1756    Android using openssl.
1757
1758    The input file must have the extension "pem". The output file must
1759    have the extension "der".
1760
1761    Args:
1762        in_file: The original key file.
1763        out_file: The full path to the converted key file, including
1764        filename.
1765    """
1766    asserts.assert_true(in_file.endswith(".pem"), "Input file has to be .pem.")
1767    asserts.assert_true(
1768        out_file.endswith(".der"), "Output file has to be .der.")
1769    cmd = ("openssl pkcs8 -inform PEM -in {} -outform DER -out {} -nocrypt"
1770           " -topk8").format(in_file, out_file)
1771    utils.exe_cmd(cmd)
1772
1773
1774def validate_connection(ad, ping_addr=DEFAULT_PING_ADDR):
1775    """Validate internet connection by pinging the address provided.
1776
1777    Args:
1778        ad: android_device object.
1779        ping_addr: address on internet for pinging.
1780
1781    Returns:
1782        ping output if successful, NULL otherwise.
1783    """
1784    ping = ad.droid.httpPing(ping_addr)
1785    ad.log.info("Http ping result: %s.", ping)
1786    return ping
1787
1788
1789#TODO(angli): This can only verify if an actual value is exactly the same.
1790# Would be nice to be able to verify an actual value is one of serveral.
1791def verify_wifi_connection_info(ad, expected_con):
1792    """Verifies that the information of the currently connected wifi network is
1793    as expected.
1794
1795    Args:
1796        expected_con: A dict representing expected key-value pairs for wifi
1797            connection. e.g. {"SSID": "test_wifi"}
1798    """
1799    current_con = ad.droid.wifiGetConnectionInfo()
1800    case_insensitive = ["BSSID", "supplicant_state"]
1801    ad.log.debug("Current connection: %s", current_con)
1802    for k, expected_v in expected_con.items():
1803        # Do not verify authentication related fields.
1804        if k == "password":
1805            continue
1806        msg = "Field %s does not exist in wifi connection info %s." % (
1807            k, current_con)
1808        if k not in current_con:
1809            raise signals.TestFailure(msg)
1810        actual_v = current_con[k]
1811        if k in case_insensitive:
1812            actual_v = actual_v.lower()
1813            expected_v = expected_v.lower()
1814        msg = "Expected %s to be %s, actual %s is %s." % (k, expected_v, k,
1815                                                          actual_v)
1816        if actual_v != expected_v:
1817            raise signals.TestFailure(msg)
1818
1819
1820def check_autoconnect_to_open_network(ad, conn_timeout=WIFI_CONNECTION_TIMEOUT_DEFAULT):
1821    """Connects to any open WiFI AP
1822     Args:
1823         timeout value in sec to wait for UE to connect to a WiFi AP
1824     Returns:
1825         True if UE connects to WiFi AP (supplicant_state = completed)
1826         False if UE fails to complete connection within WIFI_CONNECTION_TIMEOUT time.
1827    """
1828    if ad.droid.wifiCheckState():
1829        return True
1830    ad.droid.wifiToggleState()
1831    wifi_connection_state = None
1832    timeout = time.time() + conn_timeout
1833    while wifi_connection_state != "completed":
1834        wifi_connection_state = ad.droid.wifiGetConnectionInfo()[
1835            'supplicant_state']
1836        if time.time() > timeout:
1837            ad.log.warning("Failed to connect to WiFi AP")
1838            return False
1839    return True
1840
1841
1842def expand_enterprise_config_by_phase2(config):
1843    """Take an enterprise config and generate a list of configs, each with
1844    a different phase2 auth type.
1845
1846    Args:
1847        config: A dict representing enterprise config.
1848
1849    Returns
1850        A list of enterprise configs.
1851    """
1852    results = []
1853    phase2_types = WifiEnums.EapPhase2
1854    if config[WifiEnums.Enterprise.EAP] == WifiEnums.Eap.PEAP:
1855        # Skip unsupported phase2 types for PEAP.
1856        phase2_types = [WifiEnums.EapPhase2.GTC, WifiEnums.EapPhase2.MSCHAPV2]
1857    for phase2_type in phase2_types:
1858        # Skip a special case for passpoint TTLS.
1859        if (WifiEnums.Enterprise.FQDN in config and
1860                phase2_type == WifiEnums.EapPhase2.GTC):
1861            continue
1862        c = dict(config)
1863        c[WifiEnums.Enterprise.PHASE2] = phase2_type.value
1864        results.append(c)
1865    return results
1866
1867
1868def generate_eap_test_name(config, ad=None):
1869    """ Generates a test case name based on an EAP configuration.
1870
1871    Args:
1872        config: A dict representing an EAP credential.
1873        ad object: Redundant but required as the same param is passed
1874                   to test_func in run_generated_tests
1875
1876    Returns:
1877        A string representing the name of a generated EAP test case.
1878    """
1879    eap = WifiEnums.Eap
1880    eap_phase2 = WifiEnums.EapPhase2
1881    Ent = WifiEnums.Enterprise
1882    name = "test_connect-"
1883    eap_name = ""
1884    for e in eap:
1885        if e.value == config[Ent.EAP]:
1886            eap_name = e.name
1887            break
1888    if "peap0" in config[WifiEnums.SSID_KEY].lower():
1889        eap_name = "PEAP0"
1890    if "peap1" in config[WifiEnums.SSID_KEY].lower():
1891        eap_name = "PEAP1"
1892    name += eap_name
1893    if Ent.PHASE2 in config:
1894        for e in eap_phase2:
1895            if e.value == config[Ent.PHASE2]:
1896                name += "-{}".format(e.name)
1897                break
1898    return name
1899
1900
1901def group_attenuators(attenuators):
1902    """Groups a list of attenuators into attenuator groups for backward
1903    compatibility reasons.
1904
1905    Most legacy Wi-Fi setups have two attenuators each connected to a separate
1906    AP. The new Wi-Fi setup has four attenuators, each connected to one channel
1907    on an AP, so two of them are connected to one AP.
1908
1909    To make the existing scripts work in the new setup, when the script needs
1910    to attenuate one AP, it needs to set attenuation on both attenuators
1911    connected to the same AP.
1912
1913    This function groups attenuators properly so the scripts work in both
1914    legacy and new Wi-Fi setups.
1915
1916    Args:
1917        attenuators: A list of attenuator objects, either two or four in length.
1918
1919    Raises:
1920        signals.TestFailure is raised if the attenuator list does not have two
1921        or four objects.
1922    """
1923    attn0 = attenuator.AttenuatorGroup("AP0")
1924    attn1 = attenuator.AttenuatorGroup("AP1")
1925    # Legacy testbed setup has two attenuation channels.
1926    num_of_attns = len(attenuators)
1927    if num_of_attns == 2:
1928        attn0.add(attenuators[0])
1929        attn1.add(attenuators[1])
1930    elif num_of_attns == 4:
1931        attn0.add(attenuators[0])
1932        attn0.add(attenuators[1])
1933        attn1.add(attenuators[2])
1934        attn1.add(attenuators[3])
1935    else:
1936        asserts.fail(("Either two or four attenuators are required for this "
1937                      "test, but found %s") % num_of_attns)
1938    return [attn0, attn1]
1939
1940
1941def set_attns(attenuator, attn_val_name):
1942    """Sets attenuation values on attenuators used in this test.
1943
1944    Args:
1945        attenuator: The attenuator object.
1946        attn_val_name: Name of the attenuation value pair to use.
1947    """
1948    logging.info("Set attenuation values to %s", roaming_attn[attn_val_name])
1949    try:
1950        attenuator[0].set_atten(roaming_attn[attn_val_name][0])
1951        attenuator[1].set_atten(roaming_attn[attn_val_name][1])
1952        attenuator[2].set_atten(roaming_attn[attn_val_name][2])
1953        attenuator[3].set_atten(roaming_attn[attn_val_name][3])
1954    except:
1955        logging.exception("Failed to set attenuation values %s.",
1956                       attn_val_name)
1957        raise
1958
1959
1960def trigger_roaming_and_validate(dut, attenuator, attn_val_name, expected_con):
1961    """Sets attenuators to trigger roaming and validate the DUT connected
1962    to the BSSID expected.
1963
1964    Args:
1965        attenuator: The attenuator object.
1966        attn_val_name: Name of the attenuation value pair to use.
1967        expected_con: The network information of the expected network.
1968    """
1969    expected_con = {
1970        WifiEnums.SSID_KEY: expected_con[WifiEnums.SSID_KEY],
1971        WifiEnums.BSSID_KEY: expected_con["bssid"],
1972    }
1973    set_attns(attenuator, attn_val_name)
1974    logging.info("Wait %ss for roaming to finish.", ROAMING_TIMEOUT)
1975    time.sleep(ROAMING_TIMEOUT)
1976
1977    verify_wifi_connection_info(dut, expected_con)
1978    expected_bssid = expected_con[WifiEnums.BSSID_KEY]
1979    logging.info("Roamed to %s successfully", expected_bssid)
1980    if not validate_connection(dut):
1981        raise signals.TestFailure("Fail to connect to internet on %s" %
1982                                      expected_bssid)
1983
1984def create_softap_config():
1985    """Create a softap config with random ssid and password."""
1986    ap_ssid = "softap_" + utils.rand_ascii_str(8)
1987    ap_password = utils.rand_ascii_str(8)
1988    logging.info("softap setup: %s %s", ap_ssid, ap_password)
1989    config = {
1990        WifiEnums.SSID_KEY: ap_ssid,
1991        WifiEnums.PWD_KEY: ap_password,
1992    }
1993    return config
1994
1995
1996def start_softap_and_verify(ad, band):
1997    """Bring-up softap and verify AP mode and in scan results.
1998
1999    Args:
2000        band: The band to use for softAP.
2001
2002    Returns: dict, the softAP config.
2003
2004    """
2005    config = create_softap_config()
2006    start_wifi_tethering(ad.dut,
2007                         config[WifiEnums.SSID_KEY],
2008                         config[WifiEnums.PWD_KEY], band=band)
2009    asserts.assert_true(ad.dut.droid.wifiIsApEnabled(),
2010                         "SoftAp is not reported as running")
2011    start_wifi_connection_scan_and_ensure_network_found(ad.dut_client,
2012        config[WifiEnums.SSID_KEY])
2013    return config
2014
2015
2016def start_pcap(pcap, wifi_band, test_name):
2017    """Start packet capture in monitor mode.
2018
2019    Args:
2020        pcap: packet capture object
2021        wifi_band: '2g' or '5g' or 'dual'
2022        test_name: test name to be used for pcap file name
2023
2024    Returns:
2025        Dictionary with wifi band as key and the tuple
2026        (pcap Process object, log directory) as the value
2027    """
2028    log_dir = os.path.join(
2029        context.get_current_context().get_full_output_path(), 'PacketCapture')
2030    utils.create_dir(log_dir)
2031    if wifi_band == 'dual':
2032        bands = [BAND_2G, BAND_5G]
2033    else:
2034        bands = [wifi_band]
2035    procs = {}
2036    for band in bands:
2037        proc = pcap.start_packet_capture(band, log_dir, test_name)
2038        procs[band] = (proc, os.path.join(log_dir, test_name))
2039    return procs
2040
2041
2042def stop_pcap(pcap, procs, test_status=None):
2043    """Stop packet capture in monitor mode.
2044
2045    Since, the pcap logs in monitor mode can be very large, we will
2046    delete them if they are not required. 'test_status' if True, will delete
2047    the pcap files. If False, we will keep them.
2048
2049    Args:
2050        pcap: packet capture object
2051        procs: dictionary returned by start_pcap
2052        test_status: status of the test case
2053    """
2054    for proc, fname in procs.values():
2055        pcap.stop_packet_capture(proc)
2056
2057    if test_status:
2058        shutil.rmtree(os.path.dirname(fname))
2059
2060def verify_mac_not_found_in_pcap(mac, packets):
2061    """Verify that a mac address is not found in the captured packets.
2062
2063    Args:
2064        mac: string representation of the mac address
2065        packets: packets obtained by rdpcap(pcap_fname)
2066    """
2067    for pkt in packets:
2068        logging.debug("Packet Summary = %s", pkt.summary())
2069        if mac in pkt.summary():
2070            asserts.fail("Caught Factory MAC: %s in packet sniffer."
2071                         "Packet = %s" % (mac, pkt.show()))
2072
2073def verify_mac_is_found_in_pcap(mac, packets):
2074    """Verify that a mac address is found in the captured packets.
2075
2076    Args:
2077        mac: string representation of the mac address
2078        packets: packets obtained by rdpcap(pcap_fname)
2079    """
2080    for pkt in packets:
2081        if mac in pkt.summary():
2082            return
2083    asserts.fail("Did not find MAC = %s in packet sniffer." % mac)
2084
2085def start_cnss_diags(ads):
2086    for ad in ads:
2087        start_cnss_diag(ad)
2088
2089
2090def start_cnss_diag(ad):
2091    """Start cnss_diag to record extra wifi logs
2092
2093    Args:
2094        ad: android device object.
2095    """
2096    if ad.model in wifi_constants.DEVICES_USING_LEGACY_PROP:
2097        prop = wifi_constants.LEGACY_CNSS_DIAG_PROP
2098    else:
2099        prop = wifi_constants.CNSS_DIAG_PROP
2100    if ad.adb.getprop(prop) != 'true':
2101        ad.adb.shell("find /data/vendor/wifi/cnss_diag/wlan_logs/ -type f -delete")
2102        ad.adb.shell("setprop %s true" % prop, ignore_status=True)
2103
2104
2105def stop_cnss_diags(ads):
2106    for ad in ads:
2107        stop_cnss_diag(ad)
2108
2109
2110def stop_cnss_diag(ad):
2111    """Stops cnss_diag
2112
2113    Args:
2114        ad: android device object.
2115    """
2116    if ad.model in wifi_constants.DEVICES_USING_LEGACY_PROP:
2117        prop = wifi_constants.LEGACY_CNSS_DIAG_PROP
2118    else:
2119        prop = wifi_constants.CNSS_DIAG_PROP
2120    ad.adb.shell("setprop %s false" % prop, ignore_status=True)
2121
2122
2123def get_cnss_diag_log(ad, test_name=""):
2124    """Pulls the cnss_diag logs in the wlan_logs dir
2125    Args:
2126        ad: android device object.
2127        test_name: test case name
2128    """
2129    logs = ad.get_file_names("/data/vendor/wifi/cnss_diag/wlan_logs/")
2130    if logs:
2131        ad.log.info("Pulling cnss_diag logs %s", logs)
2132        log_path = os.path.join(ad.device_log_path, "CNSS_DIAG_%s" % ad.serial)
2133        utils.create_dir(log_path)
2134        ad.pull_files(logs, log_path)
2135
2136
2137LinkProbeResult = namedtuple('LinkProbeResult', (
2138    'is_success', 'stdout', 'elapsed_time', 'failure_reason'))
2139
2140
2141def send_link_probe(ad):
2142    """Sends a link probe to the currently connected AP, and returns whether the
2143    probe succeeded or not.
2144
2145    Args:
2146         ad: android device object
2147    Returns:
2148        LinkProbeResult namedtuple
2149    """
2150    stdout = ad.adb.shell('cmd wifi send-link-probe')
2151    asserts.assert_false('Error' in stdout or 'Exception' in stdout,
2152                         'Exception while sending link probe: ' + stdout)
2153
2154    is_success = False
2155    elapsed_time = None
2156    failure_reason = None
2157    if 'succeeded' in stdout:
2158        is_success = True
2159        elapsed_time = next(
2160            (int(token) for token in stdout.split() if token.isdigit()), None)
2161    elif 'failed with reason' in stdout:
2162        failure_reason = next(
2163            (int(token) for token in stdout.split() if token.isdigit()), None)
2164    else:
2165        asserts.fail('Unexpected link probe result: ' + stdout)
2166
2167    return LinkProbeResult(
2168        is_success=is_success, stdout=stdout,
2169        elapsed_time=elapsed_time, failure_reason=failure_reason)
2170
2171
2172def send_link_probes(ad, num_probes, delay_sec):
2173    """Sends a sequence of link probes to the currently connected AP, and
2174    returns whether the probes succeeded or not.
2175
2176    Args:
2177         ad: android device object
2178         num_probes: number of probes to perform
2179         delay_sec: delay time between probes, in seconds
2180    Returns:
2181        List[LinkProbeResult] one LinkProbeResults for each probe
2182    """
2183    logging.info('Sending link probes')
2184    results = []
2185    for _ in range(num_probes):
2186        # send_link_probe() will also fail the test if it sees an exception
2187        # in the stdout of the adb shell command
2188        result = send_link_probe(ad)
2189        logging.info('link probe results: ' + str(result))
2190        results.append(result)
2191        time.sleep(delay_sec)
2192
2193    return results
2194
2195
2196def ap_setup(test, index, ap, network, bandwidth=80, channel=6):
2197        """Set up the AP with provided network info.
2198
2199        Args:
2200            test: the calling test class object.
2201            index: int, index of the AP.
2202            ap: access_point object of the AP.
2203            network: dict with information of the network, including ssid,
2204                     password and bssid.
2205            bandwidth: the operation bandwidth for the AP, default 80MHz.
2206            channel: the channel number for the AP.
2207        Returns:
2208            brconfigs: the bridge interface configs
2209        """
2210        bss_settings = []
2211        ssid = network[WifiEnums.SSID_KEY]
2212        test.access_points[index].close()
2213        time.sleep(5)
2214
2215        # Configure AP as required.
2216        if "password" in network.keys():
2217            password = network["password"]
2218            security = hostapd_security.Security(
2219                security_mode="wpa", password=password)
2220        else:
2221            security = hostapd_security.Security(security_mode=None, password=None)
2222        config = hostapd_ap_preset.create_ap_preset(
2223                                                    channel=channel,
2224                                                    ssid=ssid,
2225                                                    security=security,
2226                                                    bss_settings=bss_settings,
2227                                                    vht_bandwidth=bandwidth,
2228                                                    profile_name='whirlwind',
2229                                                    iface_wlan_2g=ap.wlan_2g,
2230                                                    iface_wlan_5g=ap.wlan_5g)
2231        ap.start_ap(config)
2232        logging.info("AP started on channel {} with SSID {}".format(channel, ssid))
2233
2234
2235def turn_ap_off(test, AP):
2236    """Bring down hostapd on the Access Point.
2237    Args:
2238        test: The test class object.
2239        AP: int, indicating which AP to turn OFF.
2240    """
2241    hostapd_2g = test.access_points[AP-1]._aps['wlan0'].hostapd
2242    if hostapd_2g.is_alive():
2243        hostapd_2g.stop()
2244        logging.debug('Turned WLAN0 AP%d off' % AP)
2245    hostapd_5g = test.access_points[AP-1]._aps['wlan1'].hostapd
2246    if hostapd_5g.is_alive():
2247        hostapd_5g.stop()
2248        logging.debug('Turned WLAN1 AP%d off' % AP)
2249
2250
2251def turn_ap_on(test, AP):
2252    """Bring up hostapd on the Access Point.
2253    Args:
2254        test: The test class object.
2255        AP: int, indicating which AP to turn ON.
2256    """
2257    hostapd_2g = test.access_points[AP-1]._aps['wlan0'].hostapd
2258    if not hostapd_2g.is_alive():
2259        hostapd_2g.start(hostapd_2g.config)
2260        logging.debug('Turned WLAN0 AP%d on' % AP)
2261    hostapd_5g = test.access_points[AP-1]._aps['wlan1'].hostapd
2262    if not hostapd_5g.is_alive():
2263        hostapd_5g.start(hostapd_5g.config)
2264        logging.debug('Turned WLAN1 AP%d on' % AP)
2265