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