• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 time
18import pprint
19
20from enum import IntEnum
21from queue import Empty
22
23from acts import asserts
24from acts import signals
25from acts.logger import LoggerProxy
26from acts.test_utils.tel import tel_defines
27from acts.utils import exe_cmd
28from acts.utils import require_sl4a
29from acts.utils import sync_device_time
30from acts.utils import trim_model_name
31
32log = LoggerProxy()
33
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
38
39# The currently supported devices that existed before release
40#TODO: (navtejsingh) Need to clean up the below lists going forward
41K_DEVICES = ["hammerhead", "razor", "razorg"]
42L_DEVICES = ["shamu", "ryu"]
43L_TAP_DEVICES = ["volantis", "volantisg"]
44M_DEVICES = ["angler"]
45
46# Speed of light in m/s.
47SPEED_OF_LIGHT = 299792458
48
49DEFAULT_PING_ADDR = "http://www.google.com/robots.txt"
50
51class WifiEnums():
52
53    SSID_KEY = "SSID"
54    BSSID_KEY = "BSSID"
55    PWD_KEY = "password"
56    frequency_key = "frequency"
57    APBAND_KEY = "apBand"
58
59    WIFI_CONFIG_APBAND_2G = 0
60    WIFI_CONFIG_APBAND_5G = 1
61
62    WIFI_WPS_INFO_PBC     = 0;
63    WIFI_WPS_INFO_DISPLAY = 1;
64    WIFI_WPS_INFO_KEYPAD  = 2;
65    WIFI_WPS_INFO_LABEL   = 3;
66    WIFI_WPS_INFO_INVALID = 4;
67
68    class CountryCode():
69        CHINA = "CN"
70        JAPAN = "JP"
71        UK = "GB"
72        US = "US"
73        UNKNOWN = "UNKNOWN"
74
75    # Start of Macros for EAP
76    # EAP types
77    class Eap(IntEnum):
78        NONE = -1
79        PEAP = 0
80        TLS  = 1
81        TTLS = 2
82        PWD  = 3
83        SIM  = 4
84        AKA  = 5
85        AKA_PRIME = 6
86        UNAUTH_TLS = 7
87
88    # EAP Phase2 types
89    class EapPhase2(IntEnum):
90        NONE        = 0
91        PAP         = 1
92        MSCHAP      = 2
93        MSCHAPV2    = 3
94        GTC         = 4
95
96    class Enterprise:
97    # Enterprise Config Macros
98        EMPTY_VALUE      = "NULL"
99        EAP              = "eap"
100        PHASE2           = "phase2"
101        IDENTITY         = "identity"
102        ANON_IDENTITY    = "anonymous_identity"
103        PASSWORD         = "password"
104        SUBJECT_MATCH    = "subject_match"
105        ALTSUBJECT_MATCH = "altsubject_match"
106        DOM_SUFFIX_MATCH = "domain_suffix_match"
107        CLIENT_CERT      = "client_cert"
108        CA_CERT          = "ca_cert"
109        ENGINE           = "engine"
110        ENGINE_ID        = "engine_id"
111        PRIVATE_KEY_ID   = "key_id"
112        REALM            = "realm"
113        PLMN             = "plmn"
114        FQDN             = "FQDN"
115        FRIENDLY_NAME    = "providerFriendlyName"
116        ROAMING_IDS      = "roamingConsortiumIds"
117    # End of Macros for EAP
118
119    # Macros for wifi p2p.
120    WIFI_P2P_SERVICE_TYPE_ALL = 0
121    WIFI_P2P_SERVICE_TYPE_BONJOUR = 1
122    WIFI_P2P_SERVICE_TYPE_UPNP = 2
123    WIFI_P2P_SERVICE_TYPE_VENDOR_SPECIFIC = 255
124
125    class ScanResult:
126        CHANNEL_WIDTH_20MHZ = 0
127        CHANNEL_WIDTH_40MHZ = 1
128        CHANNEL_WIDTH_80MHZ = 2
129        CHANNEL_WIDTH_160MHZ = 3
130        CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4
131
132    # Macros for wifi rtt.
133    class RttType(IntEnum):
134        TYPE_ONE_SIDED      = 1
135        TYPE_TWO_SIDED      = 2
136
137    class RttPeerType(IntEnum):
138        PEER_TYPE_AP        = 1
139        PEER_TYPE_STA       = 2 # Requires NAN.
140        PEER_P2P_GO         = 3
141        PEER_P2P_CLIENT     = 4
142        PEER_NAN            = 5
143
144    class RttPreamble(IntEnum):
145        PREAMBLE_LEGACY  = 0x01
146        PREAMBLE_HT      = 0x02
147        PREAMBLE_VHT     = 0x04
148
149    class RttBW(IntEnum):
150        BW_5_SUPPORT   = 0x01
151        BW_10_SUPPORT  = 0x02
152        BW_20_SUPPORT  = 0x04
153        BW_40_SUPPORT  = 0x08
154        BW_80_SUPPORT  = 0x10
155        BW_160_SUPPORT = 0x20
156
157    class Rtt(IntEnum):
158        STATUS_SUCCESS                  = 0
159        STATUS_FAILURE                  = 1
160        STATUS_FAIL_NO_RSP              = 2
161        STATUS_FAIL_REJECTED            = 3
162        STATUS_FAIL_NOT_SCHEDULED_YET   = 4
163        STATUS_FAIL_TM_TIMEOUT          = 5
164        STATUS_FAIL_AP_ON_DIFF_CHANNEL  = 6
165        STATUS_FAIL_NO_CAPABILITY       = 7
166        STATUS_ABORTED                  = 8
167        STATUS_FAIL_INVALID_TS          = 9
168        STATUS_FAIL_PROTOCOL            = 10
169        STATUS_FAIL_SCHEDULE            = 11
170        STATUS_FAIL_BUSY_TRY_LATER      = 12
171        STATUS_INVALID_REQ              = 13
172        STATUS_NO_WIFI                  = 14
173        STATUS_FAIL_FTM_PARAM_OVERRIDE  = 15
174
175        REASON_UNSPECIFIED              = -1
176        REASON_NOT_AVAILABLE            = -2
177        REASON_INVALID_LISTENER         = -3
178        REASON_INVALID_REQUEST          = -4
179
180    class RttParam:
181        device_type = "deviceType"
182        request_type = "requestType"
183        BSSID = "bssid"
184        channel_width = "channelWidth"
185        frequency = "frequency"
186        center_freq0 = "centerFreq0"
187        center_freq1 = "centerFreq1"
188        number_burst = "numberBurst"
189        interval = "interval"
190        num_samples_per_burst = "numSamplesPerBurst"
191        num_retries_per_measurement_frame = "numRetriesPerMeasurementFrame"
192        num_retries_per_FTMR = "numRetriesPerFTMR"
193        lci_request = "LCIRequest"
194        lcr_request = "LCRRequest"
195        burst_timeout = "burstTimeout"
196        preamble = "preamble"
197        bandwidth = "bandwidth"
198        margin = "margin"
199
200    RTT_MARGIN_OF_ERROR = {
201        RttBW.BW_80_SUPPORT: 2,
202        RttBW.BW_40_SUPPORT: 5,
203        RttBW.BW_20_SUPPORT: 5
204    }
205
206    # Macros as specified in the WifiScanner code.
207    WIFI_BAND_UNSPECIFIED = 0      # not specified
208    WIFI_BAND_24_GHZ = 1           # 2.4 GHz band
209    WIFI_BAND_5_GHZ = 2            # 5 GHz band without DFS channels
210    WIFI_BAND_5_GHZ_DFS_ONLY  = 4  # 5 GHz band with DFS channels
211    WIFI_BAND_5_GHZ_WITH_DFS  = 6  # 5 GHz band with DFS channels
212    WIFI_BAND_BOTH = 3             # both bands without DFS channels
213    WIFI_BAND_BOTH_WITH_DFS = 7    # both bands with DFS channels
214
215    REPORT_EVENT_AFTER_BUFFER_FULL = 0
216    REPORT_EVENT_AFTER_EACH_SCAN = 1
217    REPORT_EVENT_FULL_SCAN_RESULT = 2
218
219    # US Wifi frequencies
220    ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
221                          2457, 2462]
222    DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560, 5580,
223                          5600, 5620, 5640, 5660, 5680, 5700, 5720]
224    NONE_DFS_5G_FREQUENCIES = [5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805,
225                               5825]
226    ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
227
228    band_to_frequencies = {
229      WIFI_BAND_24_GHZ: ALL_2G_FREQUENCIES,
230      WIFI_BAND_5_GHZ: NONE_DFS_5G_FREQUENCIES,
231      WIFI_BAND_5_GHZ_DFS_ONLY: DFS_5G_FREQUENCIES,
232      WIFI_BAND_5_GHZ_WITH_DFS: ALL_5G_FREQUENCIES,
233      WIFI_BAND_BOTH: ALL_2G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES,
234      WIFI_BAND_BOTH_WITH_DFS: ALL_5G_FREQUENCIES + ALL_2G_FREQUENCIES
235    }
236
237    # All Wifi frequencies to channels lookup.
238    freq_to_channel = {
239        2412: 1,
240        2417: 2,
241        2422: 3,
242        2427: 4,
243        2432: 5,
244        2437: 6,
245        2442: 7,
246        2447: 8,
247        2452: 9,
248        2457: 10,
249        2462: 11,
250        2467: 12,
251        2472: 13,
252        2484: 14,
253        4915: 183,
254        4920: 184,
255        4925: 185,
256        4935: 187,
257        4940: 188,
258        4945: 189,
259        4960: 192,
260        4980: 196,
261        5035: 7,
262        5040: 8,
263        5045: 9,
264        5055: 11,
265        5060: 12,
266        5080: 16,
267        5170: 34,
268        5180: 36,
269        5190: 38,
270        5200: 40,
271        5210: 42,
272        5220: 44,
273        5230: 46,
274        5240: 48,
275        5260: 52,
276        5280: 56,
277        5300: 60,
278        5320: 64,
279        5500: 100,
280        5520: 104,
281        5540: 108,
282        5560: 112,
283        5580: 116,
284        5600: 120,
285        5620: 124,
286        5640: 128,
287        5660: 132,
288        5680: 136,
289        5700: 140,
290        5745: 149,
291        5765: 153,
292        5785: 157,
293        5805: 161,
294        5825: 165,
295    }
296
297    # All Wifi channels to frequencies lookup.
298    channel_2G_to_freq = {
299        1: 2412,
300        2: 2417,
301        3: 2422,
302        4: 2427,
303        5: 2432,
304        6: 2437,
305        7: 2442,
306        8: 2447,
307        9: 2452,
308        10: 2457,
309        11: 2462,
310        12: 2467,
311        13: 2472,
312        14: 2484
313    }
314
315    channel_5G_to_freq = {
316        183: 4915,
317        184: 4920,
318        185: 4925,
319        187: 4935,
320        188: 4940,
321        189: 4945,
322        192: 4960,
323        196: 4980,
324        7: 5035,
325        8: 5040,
326        9: 5045,
327        11: 5055,
328        12: 5060,
329        16: 5080,
330        34: 5170,
331        36: 5180,
332        38: 5190,
333        40: 5200,
334        42: 5210,
335        44: 5220,
336        46: 5230,
337        48: 5240,
338        52: 5260,
339        56: 5280,
340        60: 5300,
341        64: 5320,
342        100: 5500,
343        104: 5520,
344        108: 5540,
345        112: 5560,
346        116: 5580,
347        120: 5600,
348        124: 5620,
349        128: 5640,
350        132: 5660,
351        136: 5680,
352        140: 5700,
353        149: 5745,
354        153: 5765,
355        157: 5785,
356        161: 5805,
357        165: 5825
358    }
359
360class WifiEventNames:
361    WIFI_CONNECTED = "WifiNetworkConnected"
362    SUPPLICANT_CON_CHANGED = "SupplicantConnectionChanged"
363    WIFI_FORGET_NW_SUCCESS = "WifiManagerForgetNetworkOnSuccess"
364
365class WifiTestUtilsError(Exception):
366    pass
367
368class WifiChannelBase:
369    ALL_2G_FREQUENCIES = []
370    DFS_5G_FREQUENCIES = []
371    NONE_DFS_5G_FREQUENCIES = []
372    ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
373    MIX_CHANNEL_SCAN = []
374
375    def band_to_freq(self, band):
376        _band_to_frequencies = {
377            WifiEnums.WIFI_BAND_24_GHZ: self.ALL_2G_FREQUENCIES,
378            WifiEnums.WIFI_BAND_5_GHZ: self.NONE_DFS_5G_FREQUENCIES,
379            WifiEnums.WIFI_BAND_5_GHZ_DFS_ONLY: self.DFS_5G_FREQUENCIES,
380            WifiEnums.WIFI_BAND_5_GHZ_WITH_DFS: self.ALL_5G_FREQUENCIES,
381            WifiEnums.WIFI_BAND_BOTH: self.ALL_2G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES,
382            WifiEnums.WIFI_BAND_BOTH_WITH_DFS: self.ALL_5G_FREQUENCIES + self.ALL_2G_FREQUENCIES
383        }
384        return _band_to_frequencies[band]
385
386class WifiChannelUS(WifiChannelBase):
387    # US Wifi frequencies
388    ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
389                          2457, 2462]
390    NONE_DFS_5G_FREQUENCIES = [5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805,
391                               5825]
392    MIX_CHANNEL_SCAN = [2412, 2437, 2462, 5180, 5200, 5280, 5260, 5300,5500, 5320,
393                        5520, 5560, 5700, 5745, 5805]
394
395    def __init__(self, model=None):
396        if model and trim_model_name(model) in K_DEVICES:
397            self.DFS_5G_FREQUENCIES = []
398            self.ALL_5G_FREQUENCIES = self.NONE_DFS_5G_FREQUENCIES
399            self.MIX_CHANNEL_SCAN = [2412, 2437, 2462, 5180, 5200, 5240, 5745, 5765]
400        elif model and trim_model_name(model) in L_DEVICES:
401            self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520,
402                                       5540, 5560, 5580, 5660, 5680, 5700]
403            self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
404        elif model and trim_model_name(model) in L_TAP_DEVICES:
405            self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520,
406                                       5540, 5560, 5580, 5660, 5680, 5700, 5720]
407            self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
408        elif model and trim_model_name(model) in M_DEVICES:
409            self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560,5580,
410                                       5600, 5620, 5640, 5660, 5680, 5700]
411            self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
412        else:
413            self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560,5580,
414                                       5600, 5620, 5640, 5660, 5680, 5700, 5720]
415            self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
416
417def match_networks(target_params, networks):
418    """Finds the WiFi networks that match a given set of parameters in a list
419    of WiFi networks.
420
421    To be considered a match, a network needs to have all the target parameters
422    and the values of those parameters need to equal to those of the target
423    parameters.
424
425    Args:
426        target_params: The target parameters to match networks against.
427        networks: A list of dict objects representing WiFi networks.
428
429    Returns:
430        The networks that match the target parameters.
431    """
432    results = []
433    for n in networks:
434        for k, v in target_params.items():
435            if k not in n:
436                continue
437            if n[k] != v:
438                continue
439            results.append(n)
440    return results
441
442def wifi_toggle_state(ad, new_state=None):
443    """Toggles the state of wifi.
444
445    Args:
446        ad: An AndroidDevice object.
447        new_state: Wifi state to set to. If None, opposite of the current state.
448
449    Returns:
450        True if the toggle was successful, False otherwise.
451    """
452    # Check if the new_state is already achieved, so we don't wait for the
453    # state change event by mistake.
454    if new_state == ad.droid.wifiCheckState():
455        return True
456    ad.droid.wifiStartTrackingStateChange()
457    log.info("Setting wifi state to {}".format(new_state))
458    ad.droid.wifiToggleState(new_state)
459    try:
460        event = ad.ed.pop_event(WifiEventNames.SUPPLICANT_CON_CHANGED, SHORT_TIMEOUT)
461        return event['data']['Connected'] == new_state
462    except Empty:
463      # Supplicant connection event is not always reliable. We double check here
464      # and call it a success as long as the new state equals the expected state.
465        return new_state == ad.droid.wifiCheckState()
466    finally:
467        ad.droid.wifiStopTrackingStateChange()
468
469def reset_wifi(ad):
470    """Clears all saved networks on a device.
471
472    Args:
473        ad: An AndroidDevice object.
474
475    Raises:
476        WifiTestUtilsError is raised if forget network operation failed.
477    """
478    ad.droid.wifiToggleState(True)
479    networks = ad.droid.wifiGetConfiguredNetworks()
480    if not networks:
481        return
482    for n in networks:
483        ad.droid.wifiForgetNetwork(n['networkId'])
484        try:
485            event = ad.ed.pop_event(WifiEventNames.WIFI_FORGET_NW_SUCCESS,
486              SHORT_TIMEOUT)
487        except Empty:
488            raise WifiTestUtilsError("Failed to remove network {}.".format(n))
489
490def wifi_forget_network(ad, net_ssid):
491    """Remove configured Wifi network on an android device.
492
493    Args:
494        ad: android_device object for forget network.
495        net_ssid: ssid of network to be forget
496
497    Raises:
498        WifiTestUtilsError is raised if forget network operation failed.
499    """
500    droid, ed = ad.droid, ad.ed
501    droid.wifiToggleState(True)
502    networks = droid.wifiGetConfiguredNetworks()
503    if not networks:
504        return
505    for n in networks:
506        if net_ssid in n[WifiEnums.SSID_KEY]:
507            droid.wifiForgetNetwork(n['networkId'])
508            try:
509                event = ed.pop_event(WifiEventNames.WIFI_FORGET_NW_SUCCESS,
510                        SHORT_TIMEOUT)
511            except Empty:
512                raise WifiTestUtilsError("Failed to remove network %s." % n)
513
514def wifi_test_device_init(ad):
515    """Initializes an android device for wifi testing.
516
517    0. Make sure SL4A connection is established on the android device.
518    1. Disable location service's WiFi scan.
519    2. Turn WiFi on.
520    3. Clear all saved networks.
521    4. Set country code to US.
522    5. Enable WiFi verbose logging.
523    6. Sync device time with computer time.
524    7. Turn off cellular data.
525    """
526    require_sl4a((ad,))
527    ad.droid.wifiScannerToggleAlwaysAvailable(False)
528    msg = "Failed to turn off location service's scan."
529    assert not ad.droid.wifiScannerIsAlwaysAvailable(), msg
530    msg = "Failed to turn WiFi on %s" % ad.serial
531    assert wifi_toggle_state(ad, True), msg
532    reset_wifi(ad)
533    msg = "Failed to clear configured networks."
534    assert not ad.droid.wifiGetConfiguredNetworks(), msg
535    ad.droid.wifiEnableVerboseLogging(1)
536    msg = "Failed to enable WiFi verbose logging."
537    assert ad.droid.wifiGetVerboseLoggingLevel() == 1, msg
538    ad.droid.wifiScannerToggleAlwaysAvailable(False)
539    # We don't verify the following settings since they are not critical.
540    sync_device_time(ad)
541    ad.droid.telephonyToggleDataConnection(False)
542    # TODO(angli): need to verify the country code was actually set. No generic
543    # way to check right now.
544    ad.adb.shell("halutil -country %s" % WifiEnums.CountryCode.US)
545
546def sort_wifi_scan_results(results, key="level"):
547    """Sort wifi scan results by key.
548
549    Args:
550        results: A list of results to sort.
551        key: Name of the field to sort the results by.
552
553    Returns:
554        A list of results in sorted order.
555    """
556    return sorted(results, lambda d: (key not in d, d[key]))
557
558def start_wifi_connection_scan(ad):
559    """Starts a wifi connection scan and wait for results to become available.
560
561    Args:
562        ad: An AndroidDevice object.
563    """
564    ad.droid.wifiStartScan()
565    ad.ed.pop_event("WifiManagerScanResultsAvailable", 60)
566
567def start_wifi_background_scan(ad, scan_setting):
568    """Starts wifi background scan.
569
570    Args:
571        ad: android_device object to initiate connection on.
572        scan_setting: A dict representing the settings of the scan.
573
574    Returns:
575        If scan was started successfully, event data of success event is returned.
576    """
577    droid, ed = ad.droids[0], ad.eds[0]
578    idx = droid.wifiScannerStartBackgroundScan(scan_setting)
579    event = ed.pop_event("WifiScannerScan{}onSuccess".format(idx),
580                         SHORT_TIMEOUT)
581    return event['data']
582
583def start_wifi_tethering(ad, ssid, password, band=None):
584    """Starts wifi tethering on an android_device.
585
586    Args:
587        ad: android_device to start wifi tethering on.
588        ssid: The SSID the soft AP should broadcast.
589        password: The password the soft AP should use.
590        band: The band the soft AP should be set on. It should be either
591            WifiEnums.WIFI_CONFIG_APBAND_2G or WifiEnums.WIFI_CONFIG_APBAND_5G.
592
593    Returns:
594        True if soft AP was started successfully, False otherwise.
595    """
596    droid, ed = ad.droid, ad.ed
597    config = {
598        WifiEnums.SSID_KEY: ssid
599    }
600    if password:
601        config[WifiEnums.PWD_KEY] = password
602    if band:
603        config[WifiEnums.APBAND_KEY] = band
604    if not droid.wifiSetWifiApConfiguration(config):
605        log.error("Failed to update WifiAp Configuration")
606        return False
607    droid.connectivityStartTethering(tel_defines.TETHERING_WIFI, False)
608    ed.pop_event("ConnectivityManagerOnTetheringStarted")
609    return True
610
611def stop_wifi_tethering(ad):
612    """Stops wifi tethering on an android_device.
613
614    Args:
615        ad: android_device to stop wifi tethering on.
616    """
617    droid, ed = ad.droid, ad.ed
618    droid.wifiStartTrackingTetherStateChange()
619    droid.connectivityStopTethering(tel_defines.TETHERING_WIFI)
620    droid.wifiSetApEnabled(False, None)
621    ed.pop_event("WifiManagerApDisabled", 30)
622    ed.wait_for_event("TetherStateChanged",
623        lambda x : not x["data"]["ACTIVE_TETHER"], 30)
624    droid.wifiStopTrackingTetherStateChange()
625
626def wifi_connect(ad, network):
627    """Connect an Android device to a wifi network.
628
629    Initiate connection to a wifi network, wait for the "connected" event, then
630    confirm the connected ssid is the one requested.
631
632    Args:
633        ad: android_device object to initiate connection on.
634        network: A dictionary representing the network to connect to. The
635            dictionary must have the key "SSID".
636    """
637    assert WifiEnums.SSID_KEY in network, ("Key '%s' must be present in "
638        "network definition.") % WifiEnums.SSID_KEY
639    ad.droid.wifiStartTrackingStateChange()
640    try:
641        assert ad.droid.wifiConnect(network), "WiFi connect returned false."
642        connect_result = ad.ed.pop_event(WifiEventNames.WIFI_CONNECTED)
643        log.debug("Connection result: %s." % connect_result)
644        expected_ssid = network[WifiEnums.SSID_KEY]
645        actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
646        assert actual_ssid == expected_ssid, ("Expected to connect to %s, "
647            "connected to %s") % (expected_ssid, actual_ssid)
648        log.info("Successfully connected to %s" % actual_ssid)
649    finally:
650        ad.droid.wifiStopTrackingStateChange()
651
652def start_wifi_single_scan(ad, scan_setting):
653    """Starts wifi single shot scan.
654
655    Args:
656        ad: android_device object to initiate connection on.
657        scan_setting: A dict representing the settings of the scan.
658
659    Returns:
660        If scan was started successfully, event data of success event is returned.
661    """
662    droid, ed = ad.droid, ad.ed
663    idx = droid.wifiScannerStartScan(scan_setting)
664    event = ed.pop_event("WifiScannerScan{}onSuccess".format(idx),
665                         SHORT_TIMEOUT)
666    log.debug("event {}".format(event))
667    return event['data']
668
669def track_connection(ad, network_ssid, check_connection_count):
670    """Track wifi connection to network changes for given number of counts
671
672    Args:
673        ad: android_device object for forget network.
674        network_ssid: network ssid to which connection would be tracked
675        check_connection_count: Integer for maximum number network connection
676            check.
677    Returns:
678
679        True if connection to given network happen, else return False.
680    """
681    droid, ed = ad.droid, ad.ed
682    droid.wifiStartTrackingStateChange()
683    while check_connection_count > 0:
684        connect_network = ed.pop_event("WifiNetworkConnected", 120)
685        log.info("connect_network {}".format(connect_network))
686        if (WifiEnums.SSID_KEY in connect_network['data']
687            and connect_network['data'][WifiEnums.SSID_KEY] == network_ssid):
688                return True
689        check_connection_count -= 1
690    droid.wifiStopTrackingStateChange()
691    return False
692
693def get_scan_time_and_channels(wifi_chs, scan_setting, stime_channel):
694    """Calculate the scan time required based on the band or channels in scan
695    setting
696
697    Args:
698        wifi_chs: Object of channels supported
699        scan_setting: scan setting used for start scan
700        stime_channel: scan time per channel
701
702    Returns:
703        scan_time: time required for completing a scan
704        scan_channels: channel used for scanning
705    """
706    scan_time = 0
707    scan_channels = []
708    if "band" in scan_setting and "channels" not in scan_setting:
709        scan_channels = wifi_chs.band_to_freq(scan_setting["band"])
710    elif "channels" in scan_setting and "band" not in scan_setting:
711        scan_channels = scan_setting["channels"]
712    scan_time = len(scan_channels) * stime_channel
713    for channel in scan_channels:
714        if channel in WifiEnums.DFS_5G_FREQUENCIES:
715            scan_time += 132 #passive scan time on DFS
716    return scan_time, scan_channels
717
718def start_wifi_track_bssid(ad, track_setting):
719    """Start tracking Bssid for the given settings.
720
721    Args:
722      ad: android_device object.
723      track_setting: Setting for which the bssid tracking should be started
724
725    Returns:
726      If tracking started successfully, event data of success event is returned.
727    """
728    droid, ed = ad.droid, ad.ed
729    idx = droid.wifiScannerStartTrackingBssids(
730        track_setting["bssidInfos"],
731        track_setting["apLostThreshold"]
732        )
733    event = ed.pop_event("WifiScannerBssid{}onSuccess".format(idx),
734                         SHORT_TIMEOUT)
735    return event['data']
736
737def convert_pem_key_to_pkcs8(in_file, out_file):
738    """Converts the key file generated by us to the format required by
739    Android using openssl.
740
741    The input file must have the extension "pem". The output file must
742    have the extension "der".
743
744    Args:
745        in_file: The original key file.
746        out_file: The full path to the converted key file, including
747        filename.
748    """
749    cmd = ("openssl pkcs8 -inform PEM -in {} -outform DER -out {} -nocrypt"
750           " -topk8").format(in_file, out_file)
751    exe_cmd(cmd)
752
753def check_internet_connection(ad, ping_addr):
754    """Validate internet connection by pinging the address provided.
755
756    Args:
757        ad: android_device object.
758        ping_addr: address on internet for pinging.
759
760    Returns:
761        True, if address ping successful
762    """
763    droid, ed = ad.droid, ad.ed
764    ping = droid.httpPing(ping_addr)
765    log.info("Http ping result: {}".format(ping))
766    return ping
767
768#TODO(angli): This can only verify if an actual value is exactly the same.
769# Would be nice to be able to verify an actual value is one of serveral.
770def verify_wifi_connection_info(ad, expected_con):
771    """Verifies that the information of the currently connected wifi network is
772    as expected.
773
774    Args:
775        expected_con: A dict representing expected key-value pairs for wifi
776            connection. e.g. {"SSID": "test_wifi"}
777    """
778    current_con = ad.droid.wifiGetConnectionInfo()
779    case_insensitive = ["BSSID", "supplicant_state"]
780    log.debug("Current connection: %s" % current_con)
781    for k, expected_v in expected_con.items():
782        # Do not verify authentication related fields.
783        if k == "password":
784            continue
785        msg = "Field %s does not exist in wifi connection info %s." % (k,
786            current_con)
787        if k not in current_con:
788            raise signals.TestFailure(msg)
789        actual_v = current_con[k]
790        if k in case_insensitive:
791            actual_v = actual_v.lower()
792            expected_v = expected_v.lower()
793        msg = "Expected %s to be %s, actual %s is %s." % (k, expected_v, k,
794            actual_v)
795        if actual_v != expected_v:
796            raise signals.TestFailure(msg)
797
798def eap_connect(config, ad, validate_con=True, ping_addr=DEFAULT_PING_ADDR):
799    """Connects to an enterprise network and verify connection.
800
801    This logic expect the enterprise network to have Internet access.
802
803    Args:
804        config: A dict representing a wifi enterprise configuration.
805        ad: The android_device to operate with.
806        validate_con: If True, validate Internet connection after connecting to
807            the network.
808
809    Returns:
810        True if the connection is successful and Internet access works.
811    """
812    droid, ed = ad.droid, ad.ed
813    start_wifi_connection_scan(ad)
814    expect_ssid = None
815    if WifiEnums.SSID_KEY in config:
816        expect_ssid = config[WifiEnums.SSID_KEY]
817        log.info("Connecting to %s." % expect_ssid)
818    else:
819        log.info("Connecting.")
820    log.debug(pprint.pformat(config, indent=4))
821    ad.droid.wifiEnterpriseConnect(config)
822    try:
823        event = ed.pop_event("WifiManagerEnterpriseConnectOnSuccess", 30)
824        log.info("Started connecting...")
825        event = ed.pop_event(WifiEventNames.WIFI_CONNECTED, 60)
826    except Empty:
827        asserts.fail("Failed to connect to %s" % config)
828    log.debug(event)
829    if expect_ssid:
830        actual_ssid = event["data"][WifiEnums.SSID_KEY]
831        asserts.assert_equal(expect_ssid, actual_ssid, "SSID mismatch.")
832    else:
833        log.info("Connected to %s." % expect_ssid)
834    if validate_con:
835        log.info("Checking Internet access.")
836        # Wait for data connection to stabilize.
837        time.sleep(4)
838        ping = ad.droid.httpPing(ping_addr)
839        log.info("Http ping result: {}".format(ping))
840        asserts.assert_true(ping, "No Internet access.")
841
842def expand_enterprise_config_by_phase2(config):
843    """Take an enterprise config and generate a list of configs, each with
844    a different phase2 auth type.
845
846    Args:
847        config: A dict representing enterprise config.
848
849    Returns
850        A list of enterprise configs.
851    """
852    results = []
853    phase2_types = WifiEnums.EapPhase2
854    if config[WifiEnums.Enterprise.EAP] == WifiEnums.Eap.PEAP:
855        # Skip unsupported phase2 types for PEAP.
856        phase2_types = [WifiEnums.EapPhase2.GTC, WifiEnums.EapPhase2.MSCHAPV2]
857    for phase2_type in phase2_types:
858        # Skip a special case for passpoint TTLS.
859        if (WifiEnums.Enterprise.FQDN in config and
860            phase2_type == WifiEnums.EapPhase2.GTC):
861            continue
862        c = dict(config)
863        c[WifiEnums.Enterprise.PHASE2] = phase2_type
864        results.append(c)
865    return results
866