1#!/usr/bin/env python3.4
2#
3#   Copyright 2016 Google, Inc.
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import logging
18import time
19import pprint
20
21from enum import IntEnum
22from queue import Empty
23
24from acts import asserts
25from acts import signals
26from acts import utils
27from acts.controllers import attenuator
28from acts.test_utils.wifi import wifi_constants
29from acts.test_utils.tel import tel_defines
30
31# Default timeout used for reboot, toggle WiFi and Airplane mode,
32# for the system to settle down after the operation.
33DEFAULT_TIMEOUT = 10
34# Number of seconds to wait for events that are supposed to happen quickly.
35# Like onSuccess for start background scan and confirmation on wifi state
36# change.
37SHORT_TIMEOUT = 30
38ROAMING_TIMEOUT = 30
39
40# Speed of light in m/s.
41SPEED_OF_LIGHT = 299792458
42
43DEFAULT_PING_ADDR = "https://www.google.com/robots.txt"
44
45roaming_attn = {
46        "AP1_on_AP2_off": [
47            0,
48            0,
49            95,
50            95
51        ],
52        "AP1_off_AP2_on": [
53            95,
54            95,
55            0,
56            0
57        ],
58        "default": [
59            0,
60            0,
61            0,
62            0
63        ]
64    }
65
66class WifiEnums():
67
68    SSID_KEY = "SSID"
69    NETID_KEY = "network_id"
70    BSSID_KEY = "BSSID"
71    PWD_KEY = "password"
72    frequency_key = "frequency"
73    APBAND_KEY = "apBand"
74    HIDDEN_KEY = "hiddenSSID"
75
76    WIFI_CONFIG_APBAND_2G = 0
77    WIFI_CONFIG_APBAND_5G = 1
78    WIFI_CONFIG_APBAND_AUTO = -1
79
80    WIFI_WPS_INFO_PBC = 0
81    WIFI_WPS_INFO_DISPLAY = 1
82    WIFI_WPS_INFO_KEYPAD = 2
83    WIFI_WPS_INFO_LABEL = 3
84    WIFI_WPS_INFO_INVALID = 4
85
86    class CountryCode():
87        CHINA = "CN"
88        JAPAN = "JP"
89        UK = "GB"
90        US = "US"
91        UNKNOWN = "UNKNOWN"
92
93    # Start of Macros for EAP
94    # EAP types
95    class Eap(IntEnum):
96        NONE = -1
97        PEAP = 0
98        TLS = 1
99        TTLS = 2
100        PWD = 3
101        SIM = 4
102        AKA = 5
103        AKA_PRIME = 6
104        UNAUTH_TLS = 7
105
106    # EAP Phase2 types
107    class EapPhase2(IntEnum):
108        NONE = 0
109        PAP = 1
110        MSCHAP = 2
111        MSCHAPV2 = 3
112        GTC = 4
113
114    class Enterprise:
115        # Enterprise Config Macros
116        EMPTY_VALUE = "NULL"
117        EAP = "eap"
118        PHASE2 = "phase2"
119        IDENTITY = "identity"
120        ANON_IDENTITY = "anonymous_identity"
121        PASSWORD = "password"
122        SUBJECT_MATCH = "subject_match"
123        ALTSUBJECT_MATCH = "altsubject_match"
124        DOM_SUFFIX_MATCH = "domain_suffix_match"
125        CLIENT_CERT = "client_cert"
126        CA_CERT = "ca_cert"
127        ENGINE = "engine"
128        ENGINE_ID = "engine_id"
129        PRIVATE_KEY_ID = "key_id"
130        REALM = "realm"
131        PLMN = "plmn"
132        FQDN = "FQDN"
133        FRIENDLY_NAME = "providerFriendlyName"
134        ROAMING_IDS = "roamingConsortiumIds"
135    # End of Macros for EAP
136
137    # Macros for wifi p2p.
138    WIFI_P2P_SERVICE_TYPE_ALL = 0
139    WIFI_P2P_SERVICE_TYPE_BONJOUR = 1
140    WIFI_P2P_SERVICE_TYPE_UPNP = 2
141    WIFI_P2P_SERVICE_TYPE_VENDOR_SPECIFIC = 255
142
143    class ScanResult:
144        CHANNEL_WIDTH_20MHZ = 0
145        CHANNEL_WIDTH_40MHZ = 1
146        CHANNEL_WIDTH_80MHZ = 2
147        CHANNEL_WIDTH_160MHZ = 3
148        CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4
149
150    # Macros for wifi rtt.
151    class RttType(IntEnum):
152        TYPE_ONE_SIDED = 1
153        TYPE_TWO_SIDED = 2
154
155    class RttPeerType(IntEnum):
156        PEER_TYPE_AP = 1
157        PEER_TYPE_STA = 2  # Requires NAN.
158        PEER_P2P_GO = 3
159        PEER_P2P_CLIENT = 4
160        PEER_NAN = 5
161
162    class RttPreamble(IntEnum):
163        PREAMBLE_LEGACY = 0x01
164        PREAMBLE_HT = 0x02
165        PREAMBLE_VHT = 0x04
166
167    class RttBW(IntEnum):
168        BW_5_SUPPORT = 0x01
169        BW_10_SUPPORT = 0x02
170        BW_20_SUPPORT = 0x04
171        BW_40_SUPPORT = 0x08
172        BW_80_SUPPORT = 0x10
173        BW_160_SUPPORT = 0x20
174
175    class Rtt(IntEnum):
176        STATUS_SUCCESS = 0
177        STATUS_FAILURE = 1
178        STATUS_FAIL_NO_RSP = 2
179        STATUS_FAIL_REJECTED = 3
180        STATUS_FAIL_NOT_SCHEDULED_YET = 4
181        STATUS_FAIL_TM_TIMEOUT = 5
182        STATUS_FAIL_AP_ON_DIFF_CHANNEL = 6
183        STATUS_FAIL_NO_CAPABILITY = 7
184        STATUS_ABORTED = 8
185        STATUS_FAIL_INVALID_TS = 9
186        STATUS_FAIL_PROTOCOL = 10
187        STATUS_FAIL_SCHEDULE = 11
188        STATUS_FAIL_BUSY_TRY_LATER = 12
189        STATUS_INVALID_REQ = 13
190        STATUS_NO_WIFI = 14
191        STATUS_FAIL_FTM_PARAM_OVERRIDE = 15
192
193        REASON_UNSPECIFIED = -1
194        REASON_NOT_AVAILABLE = -2
195        REASON_INVALID_LISTENER = -3
196        REASON_INVALID_REQUEST = -4
197
198    class RttParam:
199        device_type = "deviceType"
200        request_type = "requestType"
201        BSSID = "bssid"
202        channel_width = "channelWidth"
203        frequency = "frequency"
204        center_freq0 = "centerFreq0"
205        center_freq1 = "centerFreq1"
206        number_burst = "numberBurst"
207        interval = "interval"
208        num_samples_per_burst = "numSamplesPerBurst"
209        num_retries_per_measurement_frame = "numRetriesPerMeasurementFrame"
210        num_retries_per_FTMR = "numRetriesPerFTMR"
211        lci_request = "LCIRequest"
212        lcr_request = "LCRRequest"
213        burst_timeout = "burstTimeout"
214        preamble = "preamble"
215        bandwidth = "bandwidth"
216        margin = "margin"
217
218    RTT_MARGIN_OF_ERROR = {
219        RttBW.BW_80_SUPPORT: 2,
220        RttBW.BW_40_SUPPORT: 5,
221        RttBW.BW_20_SUPPORT: 5
222    }
223
224    # Macros as specified in the WifiScanner code.
225    WIFI_BAND_UNSPECIFIED = 0  # not specified
226    WIFI_BAND_24_GHZ = 1  # 2.4 GHz band
227    WIFI_BAND_5_GHZ = 2  # 5 GHz band without DFS channels
228    WIFI_BAND_5_GHZ_DFS_ONLY = 4  # 5 GHz band with DFS channels
229    WIFI_BAND_5_GHZ_WITH_DFS = 6  # 5 GHz band with DFS channels
230    WIFI_BAND_BOTH = 3  # both bands without DFS channels
231    WIFI_BAND_BOTH_WITH_DFS = 7  # both bands with DFS channels
232
233    REPORT_EVENT_AFTER_BUFFER_FULL = 0
234    REPORT_EVENT_AFTER_EACH_SCAN = 1
235    REPORT_EVENT_FULL_SCAN_RESULT = 2
236
237    SCAN_TYPE_LOW_LATENCY = 0
238    SCAN_TYPE_LOW_POWER = 1
239    SCAN_TYPE_HIGH_ACCURACY = 2
240
241    # US Wifi frequencies
242    ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
243                          2457, 2462]
244    DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560, 5580,
245                          5600, 5620, 5640, 5660, 5680, 5700, 5720]
246    NONE_DFS_5G_FREQUENCIES = [5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805,
247                               5825]
248    ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
249
250    band_to_frequencies = {
251        WIFI_BAND_24_GHZ: ALL_2G_FREQUENCIES,
252        WIFI_BAND_5_GHZ: NONE_DFS_5G_FREQUENCIES,
253        WIFI_BAND_5_GHZ_DFS_ONLY: DFS_5G_FREQUENCIES,
254        WIFI_BAND_5_GHZ_WITH_DFS: ALL_5G_FREQUENCIES,
255        WIFI_BAND_BOTH: ALL_2G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES,
256        WIFI_BAND_BOTH_WITH_DFS: ALL_5G_FREQUENCIES + ALL_2G_FREQUENCIES
257    }
258
259    # All Wifi frequencies to channels lookup.
260    freq_to_channel = {
261        2412: 1,
262        2417: 2,
263        2422: 3,
264        2427: 4,
265        2432: 5,
266        2437: 6,
267        2442: 7,
268        2447: 8,
269        2452: 9,
270        2457: 10,
271        2462: 11,
272        2467: 12,
273        2472: 13,
274        2484: 14,
275        4915: 183,
276        4920: 184,
277        4925: 185,
278        4935: 187,
279        4940: 188,
280        4945: 189,
281        4960: 192,
282        4980: 196,
283        5035: 7,
284        5040: 8,
285        5045: 9,
286        5055: 11,
287        5060: 12,
288        5080: 16,
289        5170: 34,
290        5180: 36,
291        5190: 38,
292        5200: 40,
293        5210: 42,
294        5220: 44,
295        5230: 46,
296        5240: 48,
297        5260: 52,
298        5280: 56,
299        5300: 60,
300        5320: 64,
301        5500: 100,
302        5520: 104,
303        5540: 108,
304        5560: 112,
305        5580: 116,
306        5600: 120,
307        5620: 124,
308        5640: 128,
309        5660: 132,
310        5680: 136,
311        5700: 140,
312        5745: 149,
313        5765: 153,
314        5785: 157,
315        5805: 161,
316        5825: 165,
317    }
318
319    # All Wifi channels to frequencies lookup.
320    channel_2G_to_freq = {
321        1: 2412,
322        2: 2417,
323        3: 2422,
324        4: 2427,
325        5: 2432,
326        6: 2437,
327        7: 2442,
328        8: 2447,
329        9: 2452,
330        10: 2457,
331        11: 2462,
332        12: 2467,
333        13: 2472,
334        14: 2484
335    }
336
337    channel_5G_to_freq = {
338        183: 4915,
339        184: 4920,
340        185: 4925,
341        187: 4935,
342        188: 4940,
343        189: 4945,
344        192: 4960,
345        196: 4980,
346        7: 5035,
347        8: 5040,
348        9: 5045,
349        11: 5055,
350        12: 5060,
351        16: 5080,
352        34: 5170,
353        36: 5180,
354        38: 5190,
355        40: 5200,
356        42: 5210,
357        44: 5220,
358        46: 5230,
359        48: 5240,
360        52: 5260,
361        56: 5280,
362        60: 5300,
363        64: 5320,
364        100: 5500,
365        104: 5520,
366        108: 5540,
367        112: 5560,
368        116: 5580,
369        120: 5600,
370        124: 5620,
371        128: 5640,
372        132: 5660,
373        136: 5680,
374        140: 5700,
375        149: 5745,
376        153: 5765,
377        157: 5785,
378        161: 5805,
379        165: 5825
380    }
381
382
383class WifiChannelBase:
384    ALL_2G_FREQUENCIES = []
385    DFS_5G_FREQUENCIES = []
386    NONE_DFS_5G_FREQUENCIES = []
387    ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
388    MIX_CHANNEL_SCAN = []
389
390    def band_to_freq(self, band):
391        _band_to_frequencies = {
392            WifiEnums.WIFI_BAND_24_GHZ: self.ALL_2G_FREQUENCIES,
393            WifiEnums.WIFI_BAND_5_GHZ: self.NONE_DFS_5G_FREQUENCIES,
394            WifiEnums.WIFI_BAND_5_GHZ_DFS_ONLY: self.DFS_5G_FREQUENCIES,
395            WifiEnums.WIFI_BAND_5_GHZ_WITH_DFS: self.ALL_5G_FREQUENCIES,
396            WifiEnums.WIFI_BAND_BOTH:
397            self.ALL_2G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES,
398            WifiEnums.WIFI_BAND_BOTH_WITH_DFS:
399            self.ALL_5G_FREQUENCIES + self.ALL_2G_FREQUENCIES
400        }
401        return _band_to_frequencies[band]
402
403
404class WifiChannelUS(WifiChannelBase):
405    # US Wifi frequencies
406    ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
407                          2457, 2462]
408    NONE_DFS_5G_FREQUENCIES = [5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805,
409                               5825]
410    MIX_CHANNEL_SCAN = [2412, 2437, 2462, 5180, 5200, 5280, 5260, 5300, 5500,
411                        5320, 5520, 5560, 5700, 5745, 5805]
412
413    def __init__(self, model=None):
414        self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520,
415                                   5540, 5560, 5580, 5600, 5620, 5640,
416                                   5660, 5680, 5700, 5720]
417        self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
418
419class WifiReferenceNetworks:
420    """ Class to parse and return networks of different band and
421        auth type from reference_networks
422    """
423    def __init__(self, obj):
424        self.reference_networks = obj
425        self.WIFI_2G = "2g"
426        self.WIFI_5G = "5g"
427
428        self.secure_networks_2g = []
429        self.secure_networks_5g = []
430        self.open_networks_2g = []
431        self.open_networks_5g = []
432        self._parse_networks()
433
434    def _parse_networks(self):
435        for network in self.reference_networks:
436            for key in network:
437                if key == self.WIFI_2G:
438                    if "password" in network[key]:
439                        self.secure_networks_2g.append(network[key])
440                    else:
441                        self.open_networks_2g.append(network[key])
442                else:
443                    if "password" in network[key]:
444                        self.secure_networks_5g.append(network[key])
445                    else:
446                        self.open_networks_5g.append(network[key])
447
448    def return_2g_secure_networks(self):
449        return self.secure_networks_2g
450
451    def return_5g_secure_networks(self):
452        return self.secure_networks_5g
453
454    def return_2g_open_networks(self):
455        return self.open_networks_2g
456
457    def return_5g_open_networks(self):
458        return self.open_networks_5g
459
460    def return_secure_networks(self):
461        return self.secure_networks_2g + self.secure_networks_5g
462
463    def return_open_networks(self):
464        return self.open_networks_2g + self.open_networks_5g
465
466
467def _assert_on_fail_handler(func, assert_on_fail, *args, **kwargs):
468    """Wrapper function that handles the bahevior of assert_on_fail.
469
470    When assert_on_fail is True, let all test signals through, which can
471    terminate test cases directly. When assert_on_fail is False, the wrapper
472    raises no test signals and reports operation status by returning True or
473    False.
474
475    Args:
476        func: The function to wrap. This function reports operation status by
477              raising test signals.
478        assert_on_fail: A boolean that specifies if the output of the wrapper
479                        is test signal based or return value based.
480        args: Positional args for func.
481        kwargs: Name args for func.
482
483    Returns:
484        If assert_on_fail is True, returns True/False to signal operation
485        status, otherwise return nothing.
486    """
487    try:
488        func(*args, **kwargs)
489        if not assert_on_fail:
490            return True
491    except signals.TestSignal:
492        if assert_on_fail:
493            raise
494        return False
495
496
497def assert_network_in_list(target, network_list):
498    """Makes sure a specified target Wi-Fi network exists in a list of Wi-Fi
499    networks.
500
501    Args:
502        target: A dict representing a Wi-Fi network.
503                E.g. {WifiEnums.SSID_KEY: "SomeNetwork"}
504        network_list: A list of dicts, each representing a Wi-Fi network.
505    """
506    match_results = match_networks(target, network_list)
507    asserts.assert_true(
508        match_results, "Target network %s, does not exist in network list %s" %
509        (target, network_list))
510
511
512def match_networks(target_params, networks):
513    """Finds the WiFi networks that match a given set of parameters in a list
514    of WiFi networks.
515
516    To be considered a match, the network should contain every key-value pair
517    of target_params
518
519    Args:
520        target_params: A dict with 1 or more key-value pairs representing a Wi-Fi network.
521                       E.g { 'SSID': 'wh_ap1_5g', 'BSSID': '30:b5:c2:33:e4:47' }
522        networks: A list of dict objects representing WiFi networks.
523
524    Returns:
525        The networks that match the target parameters.
526    """
527    results = []
528    asserts.assert_true(target_params,
529                        "Expected networks object 'target_params' is empty")
530    for n in networks:
531        add_network = 1
532        for k, v in target_params.items():
533            if k not in n:
534                add_network = 0
535                break
536            if n[k] != v:
537                add_network = 0
538                break
539        if add_network:
540            results.append(n)
541    return results
542
543def wait_for_wifi_state(ad, state, assert_on_fail=True):
544    """Waits for the device to transition to the specified wifi state
545
546    Args:
547        ad: An AndroidDevice object.
548        state: Wifi state to wait for.
549        assert_on_fail: If True, error checks in this function will raise test
550                        failure signals.
551
552    Returns:
553        If assert_on_fail is False, function returns True if the device transitions
554        to the specified state, False otherwise. If assert_on_fail is True, no return value.
555    """
556    return _assert_on_fail_handler(
557        _wait_for_wifi_state, assert_on_fail, ad, state=state)
558
559
560def _wait_for_wifi_state(ad, state):
561    """Toggles the state of wifi.
562
563    TestFailure signals are raised when something goes wrong.
564
565    Args:
566        ad: An AndroidDevice object.
567        state: Wifi state to wait for.
568    """
569    if state == ad.droid.wifiCheckState():
570        # Check if the state is already achieved, so we don't wait for the
571        # state change event by mistake.
572        return
573    ad.droid.wifiStartTrackingStateChange()
574    fail_msg = "Device did not transition to Wi-Fi state to %s on %s." % (state,
575                                                           ad.serial)
576    try:
577        ad.ed.wait_for_event(wifi_constants.WIFI_STATE_CHANGED,
578                             lambda x: x["data"]["enabled"] == state,
579                             SHORT_TIMEOUT)
580    except Empty:
581        asserts.assert_equal(state, ad.droid.wifiCheckState(), fail_msg)
582    finally:
583        ad.droid.wifiStopTrackingStateChange()
584
585def wifi_toggle_state(ad, new_state=None, assert_on_fail=True):
586    """Toggles the state of wifi.
587
588    Args:
589        ad: An AndroidDevice object.
590        new_state: Wifi state to set to. If None, opposite of the current state.
591        assert_on_fail: If True, error checks in this function will raise test
592                        failure signals.
593
594    Returns:
595        If assert_on_fail is False, function returns True if the toggle was
596        successful, False otherwise. If assert_on_fail is True, no return value.
597    """
598    return _assert_on_fail_handler(
599        _wifi_toggle_state, assert_on_fail, ad, new_state=new_state)
600
601
602def _wifi_toggle_state(ad, new_state=None):
603    """Toggles the state of wifi.
604
605    TestFailure signals are raised when something goes wrong.
606
607    Args:
608        ad: An AndroidDevice object.
609        new_state: The state to set Wi-Fi to. If None, opposite of the current
610                   state will be set.
611    """
612    if new_state is None:
613        new_state = not ad.droid.wifiCheckState()
614    elif new_state == ad.droid.wifiCheckState():
615        # Check if the new_state is already achieved, so we don't wait for the
616        # state change event by mistake.
617        return
618    ad.droid.wifiStartTrackingStateChange()
619    ad.log.info("Setting Wi-Fi state to %s.", new_state)
620    ad.ed.clear_all_events()
621    # Setting wifi state.
622    ad.droid.wifiToggleState(new_state)
623    fail_msg = "Failed to set Wi-Fi state to %s on %s." % (new_state,
624                                                           ad.serial)
625    try:
626        ad.ed.wait_for_event(wifi_constants.WIFI_STATE_CHANGED,
627                             lambda x: x["data"]["enabled"] == new_state,
628                             SHORT_TIMEOUT)
629    except Empty:
630        asserts.assert_equal(new_state, ad.droid.wifiCheckState(), fail_msg)
631    finally:
632        ad.droid.wifiStopTrackingStateChange()
633
634
635def reset_wifi(ad):
636    """Clears all saved Wi-Fi networks on a device.
637
638    This will turn Wi-Fi on.
639
640    Args:
641        ad: An AndroidDevice object.
642
643    """
644    networks = ad.droid.wifiGetConfiguredNetworks()
645    if not networks:
646        return
647    for n in networks:
648        ad.droid.wifiForgetNetwork(n['networkId'])
649        try:
650            event = ad.ed.pop_event(wifi_constants.WIFI_FORGET_NW_SUCCESS,
651                                    SHORT_TIMEOUT)
652        except Empty:
653            logging.warning("Could not confirm the removal of network %s.", n)
654    # Check again to see if there's any network left.
655    asserts.assert_true(
656        not ad.droid.wifiGetConfiguredNetworks(),
657        "Failed to remove these configured Wi-Fi networks: %s" % networks)
658
659
660def toggle_airplane_mode_on_and_off(ad):
661    """Turn ON and OFF Airplane mode.
662
663    ad: An AndroidDevice object.
664    Returns: Assert if turning on/off Airplane mode fails.
665
666    """
667    ad.log.debug("Toggling Airplane mode ON.")
668    asserts.assert_true(
669        utils.force_airplane_mode(ad, True),
670        "Can not turn on airplane mode on: %s" % ad.serial)
671    time.sleep(DEFAULT_TIMEOUT)
672    ad.log.debug("Toggling Airplane mode OFF.")
673    asserts.assert_true(
674        utils.force_airplane_mode(ad, False),
675        "Can not turn on airplane mode on: %s" % ad.serial)
676    time.sleep(DEFAULT_TIMEOUT)
677
678
679def toggle_wifi_off_and_on(ad):
680    """Turn OFF and ON WiFi.
681
682    ad: An AndroidDevice object.
683    Returns: Assert if turning off/on WiFi fails.
684
685    """
686    ad.log.debug("Toggling wifi OFF.")
687    wifi_toggle_state(ad, False)
688    time.sleep(DEFAULT_TIMEOUT)
689    ad.log.debug("Toggling wifi ON.")
690    wifi_toggle_state(ad, True)
691    time.sleep(DEFAULT_TIMEOUT)
692
693
694def wifi_forget_network(ad, net_ssid):
695    """Remove configured Wifi network on an android device.
696
697    Args:
698        ad: android_device object for forget network.
699        net_ssid: ssid of network to be forget
700
701    """
702    networks = ad.droid.wifiGetConfiguredNetworks()
703    if not networks:
704        return
705    for n in networks:
706        if net_ssid in n[WifiEnums.SSID_KEY]:
707            ad.droid.wifiForgetNetwork(n['networkId'])
708            try:
709                event = ad.ed.pop_event(wifi_constants.WIFI_FORGET_NW_SUCCESS,
710                                        SHORT_TIMEOUT)
711            except Empty:
712                asserts.fail("Failed to remove network %s." % n)
713
714
715def wifi_test_device_init(ad):
716    """Initializes an android device for wifi testing.
717
718    0. Make sure SL4A connection is established on the android device.
719    1. Disable location service's WiFi scan.
720    2. Turn WiFi on.
721    3. Clear all saved networks.
722    4. Set country code to US.
723    5. Enable WiFi verbose logging.
724    6. Sync device time with computer time.
725    7. Turn off cellular data.
726    8. Turn off ambient display.
727    """
728    utils.require_sl4a((ad, ))
729    ad.droid.wifiScannerToggleAlwaysAvailable(False)
730    msg = "Failed to turn off location service's scan."
731    asserts.assert_true(not ad.droid.wifiScannerIsAlwaysAvailable(), msg)
732    wifi_toggle_state(ad, True)
733    reset_wifi(ad)
734    ad.droid.wifiEnableVerboseLogging(1)
735    msg = "Failed to enable WiFi verbose logging."
736    asserts.assert_equal(ad.droid.wifiGetVerboseLoggingLevel(), 1, msg)
737    # We don't verify the following settings since they are not critical.
738    # Set wpa_supplicant log level to EXCESSIVE.
739    output = ad.adb.shell("wpa_cli -i wlan0 -p -g@android:wpa_wlan0 IFNAME="
740                          "wlan0 log_level EXCESSIVE")
741    ad.log.info("wpa_supplicant log change status: %s", output)
742    utils.sync_device_time(ad)
743    ad.droid.telephonyToggleDataConnection(False)
744    # TODO(angli): need to verify the country code was actually set. No generic
745    # way to check right now.
746    ad.adb.shell("halutil -country %s" % WifiEnums.CountryCode.US)
747    utils.set_ambient_display(ad, False)
748
749
750def start_wifi_connection_scan(ad):
751    """Starts a wifi connection scan and wait for results to become available.
752
753    Args:
754        ad: An AndroidDevice object.
755    """
756    ad.ed.clear_all_events()
757    ad.droid.wifiStartScan()
758    try:
759        ad.ed.pop_event("WifiManagerScanResultsAvailable", 60)
760    except Empty:
761        asserts.fail("Wi-Fi results did not become available within 60s.")
762
763
764def start_wifi_connection_scan_and_return_status(ad):
765    """
766    Starts a wifi connection scan and wait for results to become available
767    or a scan failure to be reported.
768
769    Args:
770        ad: An AndroidDevice object.
771    Returns:
772        True: if scan succeeded & results are available
773        False: if scan failed
774    """
775    ad.ed.clear_all_events()
776    ad.droid.wifiStartScan()
777    try:
778        events = ad.ed.pop_events(
779            "WifiManagerScan(ResultsAvailable|Failure)", 60)
780    except Empty:
781        asserts.fail(
782            "Wi-Fi scan results/failure did not become available within 60s.")
783    # If there are multiple matches, we check for atleast one success.
784    for event in events:
785        if event["name"] == "WifiManagerScanResultsAvailable":
786            return True
787        elif event["name"] == "WifiManagerScanFailure":
788            ad.log.debug("Scan failure received")
789    return False
790
791
792def start_wifi_connection_scan_and_check_for_network(ad, network_ssid,
793                                                     max_tries=3):
794    """
795    Start connectivity scans & checks if the |network_ssid| is seen in
796    scan results. The method performs a max of |max_tries| connectivity scans
797    to find the network.
798
799    Args:
800        ad: An AndroidDevice object.
801        network_ssid: SSID of the network we are looking for.
802        max_tries: Number of scans to try.
803    Returns:
804        True: if network_ssid is found in scan results.
805        False: if network_ssid is not found in scan results.
806    """
807    for num_tries in range(max_tries):
808        if start_wifi_connection_scan_and_return_status(ad):
809            scan_results = ad.droid.wifiGetScanResults()
810            match_results = match_networks(
811                {WifiEnums.SSID_KEY: network_ssid}, scan_results)
812            if len(match_results) > 0:
813                return True
814    return False
815
816
817def start_wifi_connection_scan_and_ensure_network_found(ad, network_ssid,
818                                                        max_tries=3):
819    """
820    Start connectivity scans & ensure the |network_ssid| is seen in
821    scan results. The method performs a max of |max_tries| connectivity scans
822    to find the network.
823    This method asserts on failure!
824
825    Args:
826        ad: An AndroidDevice object.
827        network_ssid: SSID of the network we are looking for.
828        max_tries: Number of scans to try.
829    """
830    ad.log.info("Starting scans to ensure %s is present", network_ssid)
831    assert_msg = "Failed to find " + network_ssid + " in scan results" \
832        " after " + str(max_tries) + " tries"
833    asserts.assert_true(start_wifi_connection_scan_and_check_for_network(
834        ad, network_ssid, max_tries), assert_msg)
835
836
837def start_wifi_connection_scan_and_ensure_network_not_found(ad, network_ssid,
838                                                            max_tries=3):
839    """
840    Start connectivity scans & ensure the |network_ssid| is not seen in
841    scan results. The method performs a max of |max_tries| connectivity scans
842    to find the network.
843    This method asserts on failure!
844
845    Args:
846        ad: An AndroidDevice object.
847        network_ssid: SSID of the network we are looking for.
848        max_tries: Number of scans to try.
849    """
850    ad.log.info("Starting scans to ensure %s is not present", network_ssid)
851    assert_msg = "Found " + network_ssid + " in scan results" \
852        " after " + str(max_tries) + " tries"
853    asserts.assert_false(start_wifi_connection_scan_and_check_for_network(
854        ad, network_ssid, max_tries), assert_msg)
855
856
857def start_wifi_background_scan(ad, scan_setting):
858    """Starts wifi background scan.
859
860    Args:
861        ad: android_device object to initiate connection on.
862        scan_setting: A dict representing the settings of the scan.
863
864    Returns:
865        If scan was started successfully, event data of success event is returned.
866    """
867    idx = ad.droid.wifiScannerStartBackgroundScan(scan_setting)
868    event = ad.ed.pop_event("WifiScannerScan{}onSuccess".format(idx),
869                            SHORT_TIMEOUT)
870    return event['data']
871
872
873def start_wifi_tethering(ad, ssid, password, band=None, hidden=None):
874    """Starts wifi tethering on an android_device.
875
876    Args:
877        ad: android_device to start wifi tethering on.
878        ssid: The SSID the soft AP should broadcast.
879        password: The password the soft AP should use.
880        band: The band the soft AP should be set on. It should be either
881            WifiEnums.WIFI_CONFIG_APBAND_2G or WifiEnums.WIFI_CONFIG_APBAND_5G.
882        hidden: boolean to indicate if the AP needs to be hidden or not.
883
884    Returns:
885        No return value. Error checks in this function will raise test failure signals
886    """
887    config = {WifiEnums.SSID_KEY: ssid}
888    if password:
889        config[WifiEnums.PWD_KEY] = password
890    if band:
891        config[WifiEnums.APBAND_KEY] = band
892    if hidden:
893      config[WifiEnums.HIDDEN_KEY] = hidden
894    asserts.assert_true(
895        ad.droid.wifiSetWifiApConfiguration(config),
896        "Failed to update WifiAp Configuration")
897    ad.droid.wifiStartTrackingTetherStateChange()
898    ad.droid.connectivityStartTethering(tel_defines.TETHERING_WIFI, False)
899    try:
900        ad.ed.pop_event("ConnectivityManagerOnTetheringStarted")
901        ad.ed.wait_for_event("TetherStateChanged",
902                             lambda x: x["data"]["ACTIVE_TETHER"], 30)
903        ad.log.debug("Tethering started successfully.")
904    except Empty:
905        msg = "Failed to receive confirmation of wifi tethering starting"
906        asserts.fail(msg)
907    finally:
908        ad.droid.wifiStopTrackingTetherStateChange()
909
910
911def stop_wifi_tethering(ad):
912    """Stops wifi tethering on an android_device.
913
914    Args:
915        ad: android_device to stop wifi tethering on.
916    """
917    ad.droid.wifiStartTrackingTetherStateChange()
918    ad.droid.connectivityStopTethering(tel_defines.TETHERING_WIFI)
919    try:
920        ad.ed.pop_event("WifiManagerApDisabled", 30)
921        ad.ed.wait_for_event("TetherStateChanged",
922                             lambda x: not x["data"]["ACTIVE_TETHER"], 30)
923    except Empty:
924        msg = "Failed to receive confirmation of wifi tethering stopping"
925        asserts.fail(msg)
926    finally:
927        ad.droid.wifiStopTrackingTetherStateChange()
928
929
930def toggle_wifi_and_wait_for_reconnection(ad,
931                                          network,
932                                          num_of_tries=1,
933                                          assert_on_fail=True):
934    """Toggle wifi state and then wait for Android device to reconnect to
935    the provided wifi network.
936
937    This expects the device to be already connected to the provided network.
938
939    Logic steps are
940     1. Ensure that we're connected to the network.
941     2. Turn wifi off.
942     3. Wait for 10 seconds.
943     4. Turn wifi on.
944     5. Wait for the "connected" event, then confirm the connected ssid is the
945        one requested.
946
947    Args:
948        ad: android_device object to initiate connection on.
949        network: A dictionary representing the network to await connection. The
950                 dictionary must have the key "SSID".
951        num_of_tries: An integer that is the number of times to try before
952                      delaring failure. Default is 1.
953        assert_on_fail: If True, error checks in this function will raise test
954                        failure signals.
955
956    Returns:
957        If assert_on_fail is False, function returns True if the toggle was
958        successful, False otherwise. If assert_on_fail is True, no return value.
959    """
960    return _assert_on_fail_handler(
961        _toggle_wifi_and_wait_for_reconnection,
962        assert_on_fail,
963        ad,
964        network,
965        num_of_tries=num_of_tries)
966
967
968def _toggle_wifi_and_wait_for_reconnection(ad, network, num_of_tries=1):
969    """Toggle wifi state and then wait for Android device to reconnect to
970    the provided wifi network.
971
972    This expects the device to be already connected to the provided network.
973
974    Logic steps are
975     1. Ensure that we're connected to the network.
976     2. Turn wifi off.
977     3. Wait for 10 seconds.
978     4. Turn wifi on.
979     5. Wait for the "connected" event, then confirm the connected ssid is the
980        one requested.
981
982    This will directly fail a test if anything goes wrong.
983
984    Args:
985        ad: android_device object to initiate connection on.
986        network: A dictionary representing the network to await connection. The
987                 dictionary must have the key "SSID".
988        num_of_tries: An integer that is the number of times to try before
989                      delaring failure. Default is 1.
990    """
991    expected_ssid = network[WifiEnums.SSID_KEY]
992    # First ensure that we're already connected to the provided network.
993    verify_con = {WifiEnums.SSID_KEY: expected_ssid}
994    verify_wifi_connection_info(ad, verify_con)
995    # Now toggle wifi state and wait for the connection event.
996    wifi_toggle_state(ad, False)
997    time.sleep(10)
998    wifi_toggle_state(ad, True)
999    ad.droid.wifiStartTrackingStateChange()
1000    try:
1001        connect_result = None
1002        for i in range(num_of_tries):
1003            try:
1004                connect_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED,
1005                                                 30)
1006                break
1007            except Empty:
1008                pass
1009        asserts.assert_true(connect_result,
1010                            "Failed to connect to Wi-Fi network %s on %s" %
1011                            (network, ad.serial))
1012        logging.debug("Connection result on %s: %s.", ad.serial,
1013                      connect_result)
1014        actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
1015        asserts.assert_equal(actual_ssid, expected_ssid,
1016                             "Connected to the wrong network on %s."
1017                             "Expected %s, but got %s." %
1018                             (ad.serial, expected_ssid, actual_ssid))
1019        logging.info("Connected to Wi-Fi network %s on %s", actual_ssid,
1020                     ad.serial)
1021    finally:
1022        ad.droid.wifiStopTrackingStateChange()
1023
1024
1025def wait_for_connect(ad, ssid=None, id=None, tries=1):
1026    """Wait for a connect event on queue and pop when available.
1027
1028    Args:
1029        ad: An Android device object.
1030        ssid: SSID of the network to connect to.
1031        id: Network Id of the network to connect to.
1032        tries: An integer that is the number of times to try before failing.
1033
1034    Returns:
1035        A dict with details of the connection data, which looks like this:
1036        {
1037         'time': 1485460337798,
1038         'name': 'WifiNetworkConnected',
1039         'data': {
1040                  'rssi': -27,
1041                  'is_24ghz': True,
1042                  'mac_address': '02:00:00:00:00:00',
1043                  'network_id': 1,
1044                  'BSSID': '30:b5:c2:33:d3:fc',
1045                  'ip_address': 117483712,
1046                  'link_speed': 54,
1047                  'supplicant_state': 'completed',
1048                  'hidden_ssid': False,
1049                  'SSID': 'wh_ap1_2g',
1050                  'is_5ghz': False}
1051        }
1052
1053    """
1054    conn_result = None
1055
1056    # If ssid and network id is None, just wait for any connect event.
1057    if id is None and ssid is None:
1058        for i in range(tries):
1059            try:
1060                conn_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED, 30)
1061                break
1062            except Empty:
1063                pass
1064    else:
1065    # If ssid or network id is specified, wait for specific connect event.
1066        for i in range(tries):
1067            try:
1068                conn_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED, 30)
1069                if id and conn_result['data'][WifiEnums.NETID_KEY] == id:
1070                    break
1071                elif ssid and conn_result['data'][WifiEnums.SSID_KEY] == ssid:
1072                    break
1073            except Empty:
1074                pass
1075
1076    return conn_result
1077
1078
1079def wait_for_disconnect(ad):
1080    """Wait for a Disconnect event from the supplicant.
1081
1082    Args:
1083        ad: Android device object.
1084
1085    """
1086    try:
1087        ad.droid.wifiStartTrackingStateChange()
1088        event = ad.ed.pop_event("WifiNetworkDisconnected", 10)
1089        ad.droid.wifiStopTrackingStateChange()
1090    except Empty:
1091        raise signals.TestFailure("Device did not disconnect from the network")
1092
1093
1094def connect_to_wifi_network(ad, network, assert_on_fail=True,
1095        check_connectivity=True):
1096    """Connection logic for open and psk wifi networks.
1097
1098    Args:
1099        ad: AndroidDevice to use for connection
1100        network: network info of the network to connect to
1101        assert_on_fail: If true, errors from wifi_connect will raise
1102                        test failure signals.
1103    """
1104    start_wifi_connection_scan_and_ensure_network_found(
1105        ad, network[WifiEnums.SSID_KEY])
1106    wifi_connect(ad,
1107                 network,
1108                 num_of_tries=3,
1109                 assert_on_fail=assert_on_fail,
1110                 check_connectivity=check_connectivity)
1111
1112
1113def connect_to_wifi_network_with_id(ad, network_id, network_ssid):
1114    """Connect to the given network using network id and verify SSID.
1115
1116    Args:
1117        network_id: int Network Id of the network.
1118        network_ssid: string SSID of the network.
1119
1120    Returns: True if connect using network id was successful;
1121             False otherwise.
1122
1123    """
1124    start_wifi_connection_scan_and_ensure_network_found(ad, network_ssid)
1125    wifi_connect_by_id(ad, network_id)
1126    connect_data = ad.droid.wifiGetConnectionInfo()
1127    connect_ssid = connect_data[WifiEnums.SSID_KEY]
1128    ad.log.debug("Expected SSID = %s Connected SSID = %s" %
1129                   (network_ssid, connect_ssid))
1130    if connect_ssid != network_ssid:
1131        return False
1132    return True
1133
1134
1135def wifi_connect(ad, network, num_of_tries=1, assert_on_fail=True,
1136        check_connectivity=True):
1137    """Connect an Android device to a wifi network.
1138
1139    Initiate connection to a wifi network, wait for the "connected" event, then
1140    confirm the connected ssid is the one requested.
1141
1142    This will directly fail a test if anything goes wrong.
1143
1144    Args:
1145        ad: android_device object to initiate connection on.
1146        network: A dictionary representing the network to connect to. The
1147                 dictionary must have the key "SSID".
1148        num_of_tries: An integer that is the number of times to try before
1149                      delaring failure. Default is 1.
1150        assert_on_fail: If True, error checks in this function will raise test
1151                        failure signals.
1152
1153    Returns:
1154        Returns a value only if assert_on_fail is false.
1155        Returns True if the connection was successful, False otherwise.
1156    """
1157    return _assert_on_fail_handler(
1158        _wifi_connect, assert_on_fail, ad, network, num_of_tries=num_of_tries,
1159          check_connectivity=check_connectivity)
1160
1161
1162def _wifi_connect(ad, network, num_of_tries=1, check_connectivity=True):
1163    """Connect an Android device to a wifi network.
1164
1165    Initiate connection to a wifi network, wait for the "connected" event, then
1166    confirm the connected ssid is the one requested.
1167
1168    This will directly fail a test if anything goes wrong.
1169
1170    Args:
1171        ad: android_device object to initiate connection on.
1172        network: A dictionary representing the network to connect to. The
1173                 dictionary must have the key "SSID".
1174        num_of_tries: An integer that is the number of times to try before
1175                      delaring failure. Default is 1.
1176    """
1177    asserts.assert_true(WifiEnums.SSID_KEY in network,
1178                        "Key '%s' must be present in network definition." %
1179                        WifiEnums.SSID_KEY)
1180    ad.droid.wifiStartTrackingStateChange()
1181    expected_ssid = network[WifiEnums.SSID_KEY]
1182    ad.droid.wifiConnectByConfig(network)
1183    ad.log.info("Starting connection process to %s", expected_ssid)
1184    try:
1185        event = ad.ed.pop_event(wifi_constants.CONNECT_BY_CONFIG_SUCCESS, 30)
1186        connect_result = wait_for_connect(ad, ssid=expected_ssid, tries=num_of_tries)
1187        asserts.assert_true(connect_result,
1188                            "Failed to connect to Wi-Fi network %s on %s" %
1189                            (network, ad.serial))
1190        ad.log.debug("Wi-Fi connection result: %s.", connect_result)
1191        actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
1192        asserts.assert_equal(actual_ssid, expected_ssid,
1193                             "Connected to the wrong network on %s." %
1194                             ad.serial)
1195        ad.log.info("Connected to Wi-Fi network %s.", actual_ssid)
1196
1197        # Wait for data connection to stabilize.
1198        time.sleep(5)
1199
1200        if check_connectivity:
1201            internet = validate_connection(ad, DEFAULT_PING_ADDR)
1202            if not internet:
1203                raise signals.TestFailure("Failed to connect to internet on %s" %
1204                                          expected_ssid)
1205    except Empty:
1206        asserts.fail("Failed to start connection process to %s on %s" %
1207                     (network, ad.serial))
1208    except Exception as error:
1209        ad.log.error("Failed to connect to %s with error %s", expected_ssid,
1210                     error)
1211        raise signals.TestFailure("Failed to connect to %s network" % network)
1212
1213    finally:
1214        ad.droid.wifiStopTrackingStateChange()
1215
1216
1217def wifi_connect_by_id(ad, network_id, num_of_tries=3, assert_on_fail=True):
1218    """Connect an Android device to a wifi network using network Id.
1219
1220    Start connection to the wifi network, with the given network Id, wait for
1221    the "connected" event, then verify the connected network is the one requested.
1222
1223    This will directly fail a test if anything goes wrong.
1224
1225    Args:
1226        ad: android_device object to initiate connection on.
1227        network_id: Integer specifying the network id of the network.
1228        num_of_tries: An integer that is the number of times to try before
1229                      delaring failure. Default is 1.
1230        assert_on_fail: If True, error checks in this function will raise test
1231                        failure signals.
1232
1233    Returns:
1234        Returns a value only if assert_on_fail is false.
1235        Returns True if the connection was successful, False otherwise.
1236    """
1237    _assert_on_fail_handler(_wifi_connect_by_id, assert_on_fail, ad,
1238                            network_id, num_of_tries)
1239
1240
1241def _wifi_connect_by_id(ad, network_id, num_of_tries=1):
1242    """Connect an Android device to a wifi network using it's network id.
1243
1244    Start connection to the wifi network, with the given network id, wait for
1245    the "connected" event, then verify the connected network is the one requested.
1246
1247    Args:
1248        ad: android_device object to initiate connection on.
1249        network_id: Integer specifying the network id of the network.
1250        num_of_tries: An integer that is the number of times to try before
1251                      delaring failure. Default is 1.
1252    """
1253    ad.droid.wifiStartTrackingStateChange()
1254    # Clear all previous events.
1255    ad.ed.clear_all_events()
1256    ad.droid.wifiConnectByNetworkId(network_id)
1257    ad.log.info("Starting connection to network with id %d", network_id)
1258    try:
1259        event = ad.ed.pop_event(wifi_constants.CONNECT_BY_NETID_SUCCESS, 60)
1260        connect_result = wait_for_connect(ad, id=network_id, tries=num_of_tries)
1261        asserts.assert_true(connect_result,
1262                            "Failed to connect to Wi-Fi network using network id")
1263        ad.log.debug("Wi-Fi connection result: %s", connect_result)
1264        actual_id = connect_result['data'][WifiEnums.NETID_KEY]
1265        asserts.assert_equal(actual_id, network_id,
1266                             "Connected to the wrong network on %s."
1267                             "Expected network id = %d, but got %d." %
1268                             (ad.serial, network_id, actual_id))
1269        expected_ssid = connect_result['data'][WifiEnums.SSID_KEY]
1270        ad.log.info("Connected to Wi-Fi network %s with %d network id.",
1271                     expected_ssid, network_id)
1272
1273        # Wait for data connection to stabilize.
1274        time.sleep(5)
1275
1276        internet = validate_connection(ad, DEFAULT_PING_ADDR)
1277        if not internet:
1278            raise signals.TestFailure("Failed to connect to internet on %s" %
1279                                      expected_ssid)
1280    except Empty:
1281        asserts.fail("Failed to connect to network with id %d on %s" %
1282                    (network_id, ad.serial))
1283    except Exception as error:
1284        ad.log.error("Failed to connect to network with id %d with error %s",
1285                      network_id, error)
1286        raise signals.TestFailure("Failed to connect to network with network"
1287                                  " id %d" % network_id)
1288    finally:
1289        ad.droid.wifiStopTrackingStateChange()
1290
1291
1292def wifi_passpoint_connect(ad, passpoint_network, num_of_tries=1,
1293                           assert_on_fail=True):
1294    """Connect an Android device to a wifi network.
1295
1296    Initiate connection to a wifi network, wait for the "connected" event, then
1297    confirm the connected ssid is the one requested.
1298
1299    This will directly fail a test if anything goes wrong.
1300
1301    Args:
1302        ad: android_device object to initiate connection on.
1303        passpoint_network: SSID of the Passpoint network to connect to.
1304        num_of_tries: An integer that is the number of times to try before
1305                      delaring failure. Default is 1.
1306        assert_on_fail: If True, error checks in this function will raise test
1307                        failure signals.
1308
1309    Returns:
1310        If assert_on_fail is False, function returns network id, if the connect was
1311        successful, False otherwise. If assert_on_fail is True, no return value.
1312    """
1313    _assert_on_fail_handler(_wifi_passpoint_connect, assert_on_fail, ad,
1314                            passpoint_network, num_of_tries = num_of_tries)
1315
1316
1317def _wifi_passpoint_connect(ad, passpoint_network, num_of_tries=1):
1318    """Connect an Android device to a wifi network.
1319
1320    Initiate connection to a wifi network, wait for the "connected" event, then
1321    confirm the connected ssid is the one requested.
1322
1323    This will directly fail a test if anything goes wrong.
1324
1325    Args:
1326        ad: android_device object to initiate connection on.
1327        passpoint_network: SSID of the Passpoint network to connect to.
1328        num_of_tries: An integer that is the number of times to try before
1329                      delaring failure. Default is 1.
1330    """
1331    ad.droid.wifiStartTrackingStateChange()
1332    expected_ssid = passpoint_network
1333    ad.log.info("Starting connection process to passpoint %s", expected_ssid)
1334
1335    try:
1336        connect_result = wait_for_connect(ad, expected_ssid, num_of_tries)
1337        asserts.assert_true(connect_result,
1338                            "Failed to connect to WiFi passpoint network %s on"
1339                            " %s" % (expected_ssid, ad.serial))
1340        ad.log.info("Wi-Fi connection result: %s.", connect_result)
1341        actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
1342        asserts.assert_equal(actual_ssid, expected_ssid,
1343                             "Connected to the wrong network on %s." % ad.serial)
1344        ad.log.info("Connected to Wi-Fi passpoint network %s.", actual_ssid)
1345
1346        # Wait for data connection to stabilize.
1347        time.sleep(5)
1348
1349        internet = validate_connection(ad, DEFAULT_PING_ADDR)
1350        if not internet:
1351            raise signals.TestFailure("Failed to connect to internet on %s" %
1352                                      expected_ssid)
1353    except Exception as error:
1354        ad.log.error("Failed to connect to passpoint network %s with error %s",
1355                      expected_ssid, error)
1356        raise signals.TestFailure("Failed to connect to %s passpoint network" %
1357                                   expected_ssid)
1358
1359    finally:
1360        ad.droid.wifiStopTrackingStateChange()
1361
1362
1363def delete_passpoint(ad, fqdn):
1364    """Delete a required Passpoint configuration."""
1365    try:
1366        ad.droid.removePasspointConfig(fqdn)
1367        return True
1368    except Exception as error:
1369        ad.log.error("Failed to remove passpoint configuration with FQDN=%s "
1370                     "and error=%s" , fqdn, error)
1371        return False
1372
1373
1374def start_wifi_single_scan(ad, scan_setting):
1375    """Starts wifi single shot scan.
1376
1377    Args:
1378        ad: android_device object to initiate connection on.
1379        scan_setting: A dict representing the settings of the scan.
1380
1381    Returns:
1382        If scan was started successfully, event data of success event is returned.
1383    """
1384    idx = ad.droid.wifiScannerStartScan(scan_setting)
1385    event = ad.ed.pop_event("WifiScannerScan%sonSuccess" % idx, SHORT_TIMEOUT)
1386    ad.log.debug("Got event %s", event)
1387    return event['data']
1388
1389
1390def track_connection(ad, network_ssid, check_connection_count):
1391    """Track wifi connection to network changes for given number of counts
1392
1393    Args:
1394        ad: android_device object for forget network.
1395        network_ssid: network ssid to which connection would be tracked
1396        check_connection_count: Integer for maximum number network connection
1397                                check.
1398    Returns:
1399        True if connection to given network happen, else return False.
1400    """
1401    ad.droid.wifiStartTrackingStateChange()
1402    while check_connection_count > 0:
1403        connect_network = ad.ed.pop_event("WifiNetworkConnected", 120)
1404        ad.log.info("Connected to network %s", connect_network)
1405        if (WifiEnums.SSID_KEY in connect_network['data'] and
1406                connect_network['data'][WifiEnums.SSID_KEY] == network_ssid):
1407            return True
1408        check_connection_count -= 1
1409    ad.droid.wifiStopTrackingStateChange()
1410    return False
1411
1412
1413def get_scan_time_and_channels(wifi_chs, scan_setting, stime_channel):
1414    """Calculate the scan time required based on the band or channels in scan
1415    setting
1416
1417    Args:
1418        wifi_chs: Object of channels supported
1419        scan_setting: scan setting used for start scan
1420        stime_channel: scan time per channel
1421
1422    Returns:
1423        scan_time: time required for completing a scan
1424        scan_channels: channel used for scanning
1425    """
1426    scan_time = 0
1427    scan_channels = []
1428    if "band" in scan_setting and "channels" not in scan_setting:
1429        scan_channels = wifi_chs.band_to_freq(scan_setting["band"])
1430    elif "channels" in scan_setting and "band" not in scan_setting:
1431        scan_channels = scan_setting["channels"]
1432    scan_time = len(scan_channels) * stime_channel
1433    for channel in scan_channels:
1434        if channel in WifiEnums.DFS_5G_FREQUENCIES:
1435            scan_time += 132  #passive scan time on DFS
1436    return scan_time, scan_channels
1437
1438
1439def start_wifi_track_bssid(ad, track_setting):
1440    """Start tracking Bssid for the given settings.
1441
1442    Args:
1443      ad: android_device object.
1444      track_setting: Setting for which the bssid tracking should be started
1445
1446    Returns:
1447      If tracking started successfully, event data of success event is returned.
1448    """
1449    idx = ad.droid.wifiScannerStartTrackingBssids(
1450        track_setting["bssidInfos"], track_setting["apLostThreshold"])
1451    event = ad.ed.pop_event("WifiScannerBssid{}onSuccess".format(idx),
1452                            SHORT_TIMEOUT)
1453    return event['data']
1454
1455
1456def convert_pem_key_to_pkcs8(in_file, out_file):
1457    """Converts the key file generated by us to the format required by
1458    Android using openssl.
1459
1460    The input file must have the extension "pem". The output file must
1461    have the extension "der".
1462
1463    Args:
1464        in_file: The original key file.
1465        out_file: The full path to the converted key file, including
1466        filename.
1467    """
1468    asserts.assert_true(in_file.endswith(".pem"), "Input file has to be .pem.")
1469    asserts.assert_true(
1470        out_file.endswith(".der"), "Output file has to be .der.")
1471    cmd = ("openssl pkcs8 -inform PEM -in {} -outform DER -out {} -nocrypt"
1472           " -topk8").format(in_file, out_file)
1473    utils.exe_cmd(cmd)
1474
1475
1476def validate_connection(ad, ping_addr=DEFAULT_PING_ADDR):
1477    """Validate internet connection by pinging the address provided.
1478
1479    Args:
1480        ad: android_device object.
1481        ping_addr: address on internet for pinging.
1482
1483    Returns:
1484        ping output if successful, NULL otherwise.
1485    """
1486    ping = ad.droid.httpPing(ping_addr)
1487    ad.log.info("Http ping result: %s.", ping)
1488    return ping
1489
1490
1491#TODO(angli): This can only verify if an actual value is exactly the same.
1492# Would be nice to be able to verify an actual value is one of serveral.
1493def verify_wifi_connection_info(ad, expected_con):
1494    """Verifies that the information of the currently connected wifi network is
1495    as expected.
1496
1497    Args:
1498        expected_con: A dict representing expected key-value pairs for wifi
1499            connection. e.g. {"SSID": "test_wifi"}
1500    """
1501    current_con = ad.droid.wifiGetConnectionInfo()
1502    case_insensitive = ["BSSID", "supplicant_state"]
1503    ad.log.debug("Current connection: %s", current_con)
1504    for k, expected_v in expected_con.items():
1505        # Do not verify authentication related fields.
1506        if k == "password":
1507            continue
1508        msg = "Field %s does not exist in wifi connection info %s." % (
1509            k, current_con)
1510        if k not in current_con:
1511            raise signals.TestFailure(msg)
1512        actual_v = current_con[k]
1513        if k in case_insensitive:
1514            actual_v = actual_v.lower()
1515            expected_v = expected_v.lower()
1516        msg = "Expected %s to be %s, actual %s is %s." % (k, expected_v, k,
1517                                                          actual_v)
1518        if actual_v != expected_v:
1519            raise signals.TestFailure(msg)
1520
1521
1522def expand_enterprise_config_by_phase2(config):
1523    """Take an enterprise config and generate a list of configs, each with
1524    a different phase2 auth type.
1525
1526    Args:
1527        config: A dict representing enterprise config.
1528
1529    Returns
1530        A list of enterprise configs.
1531    """
1532    results = []
1533    phase2_types = WifiEnums.EapPhase2
1534    if config[WifiEnums.Enterprise.EAP] == WifiEnums.Eap.PEAP:
1535        # Skip unsupported phase2 types for PEAP.
1536        phase2_types = [WifiEnums.EapPhase2.GTC, WifiEnums.EapPhase2.MSCHAPV2]
1537    for phase2_type in phase2_types:
1538        # Skip a special case for passpoint TTLS.
1539        if (WifiEnums.Enterprise.FQDN in config and
1540                phase2_type == WifiEnums.EapPhase2.GTC):
1541            continue
1542        c = dict(config)
1543        c[WifiEnums.Enterprise.PHASE2] = phase2_type.value
1544        results.append(c)
1545    return results
1546
1547
1548def generate_eap_test_name(config, ad=None):
1549    """ Generates a test case name based on an EAP configuration.
1550
1551    Args:
1552        config: A dict representing an EAP credential.
1553        ad object: Redundant but required as the same param is passed
1554                   to test_func in run_generated_tests
1555
1556    Returns:
1557        A string representing the name of a generated EAP test case.
1558    """
1559    eap = WifiEnums.Eap
1560    eap_phase2 = WifiEnums.EapPhase2
1561    Ent = WifiEnums.Enterprise
1562    name = "test_connect-"
1563    eap_name = ""
1564    for e in eap:
1565        if e.value == config[Ent.EAP]:
1566            eap_name = e.name
1567            break
1568    if "peap0" in config[WifiEnums.SSID_KEY].lower():
1569        eap_name = "PEAP0"
1570    if "peap1" in config[WifiEnums.SSID_KEY].lower():
1571        eap_name = "PEAP1"
1572    name += eap_name
1573    if Ent.PHASE2 in config:
1574        for e in eap_phase2:
1575            if e.value == config[Ent.PHASE2]:
1576                name += "-{}".format(e.name)
1577                break
1578    return name
1579
1580
1581def group_attenuators(attenuators):
1582    """Groups a list of attenuators into attenuator groups for backward
1583    compatibility reasons.
1584
1585    Most legacy Wi-Fi setups have two attenuators each connected to a separate
1586    AP. The new Wi-Fi setup has four attenuators, each connected to one channel
1587    on an AP, so two of them are connected to one AP.
1588
1589    To make the existing scripts work in the new setup, when the script needs
1590    to attenuate one AP, it needs to set attenuation on both attenuators
1591    connected to the same AP.
1592
1593    This function groups attenuators properly so the scripts work in both
1594    legacy and new Wi-Fi setups.
1595
1596    Args:
1597        attenuators: A list of attenuator objects, either two or four in length.
1598
1599    Raises:
1600        signals.TestFailure is raised if the attenuator list does not have two
1601        or four objects.
1602    """
1603    attn0 = attenuator.AttenuatorGroup("AP0")
1604    attn1 = attenuator.AttenuatorGroup("AP1")
1605    # Legacy testbed setup has two attenuation channels.
1606    num_of_attns = len(attenuators)
1607    if num_of_attns == 2:
1608        attn0.add(attenuators[0])
1609        attn1.add(attenuators[1])
1610    elif num_of_attns == 4:
1611        attn0.add(attenuators[0])
1612        attn0.add(attenuators[1])
1613        attn1.add(attenuators[2])
1614        attn1.add(attenuators[3])
1615    else:
1616        asserts.fail(("Either two or four attenuators are required for this "
1617                      "test, but found %s") % num_of_attns)
1618    return [attn0, attn1]
1619
1620def set_attns(attenuator, attn_val_name):
1621    """Sets attenuation values on attenuators used in this test.
1622
1623    Args:
1624        attenuator: The attenuator object.
1625        attn_val_name: Name of the attenuation value pair to use.
1626    """
1627    logging.info("Set attenuation values to %s", roaming_attn[attn_val_name])
1628    try:
1629        attenuator[0].set_atten(roaming_attn[attn_val_name][0])
1630        attenuator[1].set_atten(roaming_attn[attn_val_name][1])
1631        attenuator[2].set_atten(roaming_attn[attn_val_name][2])
1632        attenuator[3].set_atten(roaming_attn[attn_val_name][3])
1633    except:
1634        logging.exception("Failed to set attenuation values %s.",
1635                       attn_val_name)
1636        raise
1637
1638
1639def trigger_roaming_and_validate(dut, attenuator, attn_val_name, expected_con):
1640    """Sets attenuators to trigger roaming and validate the DUT connected
1641    to the BSSID expected.
1642
1643    Args:
1644        attenuator: The attenuator object.
1645        attn_val_name: Name of the attenuation value pair to use.
1646        expected_con: The network information of the expected network.
1647    """
1648    expected_con = {
1649        WifiEnums.SSID_KEY: expected_con[WifiEnums.SSID_KEY],
1650        WifiEnums.BSSID_KEY: expected_con["bssid"],
1651    }
1652    set_attns(attenuator, attn_val_name)
1653    logging.info("Wait %ss for roaming to finish.", ROAMING_TIMEOUT)
1654    time.sleep(ROAMING_TIMEOUT)
1655    try:
1656        # Wakeup device and verify connection.
1657        dut.droid.wakeLockAcquireBright()
1658        dut.droid.wakeUpNow()
1659        cur_con = dut.droid.wifiGetConnectionInfo()
1660        verify_wifi_connection_info(dut, expected_con)
1661        expected_bssid = expected_con[WifiEnums.BSSID_KEY]
1662        logging.info("Roamed to %s successfully", expected_bssid)
1663        if not validate_connection(dut):
1664            raise signals.TestFailure("Fail to connect to internet on %s" %
1665                                      expected_ssid)
1666    finally:
1667        dut.droid.wifiLockRelease()
1668        dut.droid.goToSleepNow()
1669
1670
1671def create_softap_config():
1672    """Create a softap config with random ssid and password."""
1673    ap_ssid = "softap_" + utils.rand_ascii_str(8)
1674    ap_password = utils.rand_ascii_str(8)
1675    logging.info("softap setup: %s %s", ap_ssid, ap_password)
1676    config = {
1677        WifiEnums.SSID_KEY: ap_ssid,
1678        WifiEnums.PWD_KEY: ap_password,
1679    }
1680    return config
1681