• 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.utils import exe_cmd
27from acts.utils import require_sl4a
28from acts.utils import sync_device_time
29from acts.utils import trim_model_name
30
31log = LoggerProxy()
32
33# Number of seconds to wait for events that are supposed to happen quickly.
34# Like onSuccess for start background scan and confirmation on wifi state
35# change.
36SHORT_TIMEOUT = 30
37
38# The currently supported devices that existed before release
39#TODO: (navtejsingh) Need to clean up the below lists going forward
40K_DEVICES = ["hammerhead", "razor", "razorg"]
41L_DEVICES = ["shamu", "ryu"]
42L_TAP_DEVICES = ["volantis", "volantisg"]
43M_DEVICES = ["angler"]
44
45# Speed of light in m/s.
46SPEED_OF_LIGHT = 299792458
47
48DEFAULT_PING_ADDR = "http://www.google.com/robots.txt"
49
50class WifiEnums():
51
52    SSID_KEY = "SSID"
53    BSSID_KEY = "BSSID"
54    PWD_KEY = "password"
55    frequency_key = "frequency"
56    APBAND_KEY = "apBand"
57
58    WIFI_CONFIG_APBAND_2G = 0
59    WIFI_CONFIG_APBAND_5G = 1
60
61    WIFI_WPS_INFO_PBC     = 0;
62    WIFI_WPS_INFO_DISPLAY = 1;
63    WIFI_WPS_INFO_KEYPAD  = 2;
64    WIFI_WPS_INFO_LABEL   = 3;
65    WIFI_WPS_INFO_INVALID = 4;
66
67    class CountryCode():
68        CHINA = "CN"
69        JAPAN = "JP"
70        UK = "GB"
71        US = "US"
72        UNKNOWN = "UNKNOWN"
73
74    # Start of Macros for EAP
75    # EAP types
76    class Eap(IntEnum):
77        NONE = -1
78        PEAP = 0
79        TLS  = 1
80        TTLS = 2
81        PWD  = 3
82        SIM  = 4
83        AKA  = 5
84        AKA_PRIME = 6
85        UNAUTH_TLS = 7
86
87    # EAP Phase2 types
88    class EapPhase2(IntEnum):
89        NONE        = 0
90        PAP         = 1
91        MSCHAP      = 2
92        MSCHAPV2    = 3
93        GTC         = 4
94
95    class Enterprise:
96    # Enterprise Config Macros
97        EMPTY_VALUE      = "NULL"
98        EAP              = "eap"
99        PHASE2           = "phase2"
100        IDENTITY         = "identity"
101        ANON_IDENTITY    = "anonymous_identity"
102        PASSWORD         = "password"
103        SUBJECT_MATCH    = "subject_match"
104        ALTSUBJECT_MATCH = "altsubject_match"
105        DOM_SUFFIX_MATCH = "domain_suffix_match"
106        CLIENT_CERT      = "client_cert"
107        CA_CERT          = "ca_cert"
108        ENGINE           = "engine"
109        ENGINE_ID        = "engine_id"
110        PRIVATE_KEY_ID   = "key_id"
111        REALM            = "realm"
112        PLMN             = "plmn"
113        FQDN             = "FQDN"
114        FRIENDLY_NAME    = "providerFriendlyName"
115        ROAMING_IDS      = "roamingConsortiumIds"
116    # End of Macros for EAP
117
118    # Macros for wifi p2p.
119    WIFI_P2P_SERVICE_TYPE_ALL = 0
120    WIFI_P2P_SERVICE_TYPE_BONJOUR = 1
121    WIFI_P2P_SERVICE_TYPE_UPNP = 2
122    WIFI_P2P_SERVICE_TYPE_VENDOR_SPECIFIC = 255
123
124    class ScanResult:
125        CHANNEL_WIDTH_20MHZ = 0
126        CHANNEL_WIDTH_40MHZ = 1
127        CHANNEL_WIDTH_80MHZ = 2
128        CHANNEL_WIDTH_160MHZ = 3
129        CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4
130
131    # Macros for wifi rtt.
132    class RttType(IntEnum):
133        TYPE_ONE_SIDED      = 1
134        TYPE_TWO_SIDED      = 2
135
136    class RttPeerType(IntEnum):
137        PEER_TYPE_AP        = 1
138        PEER_TYPE_STA       = 2 # Requires NAN.
139        PEER_P2P_GO         = 3
140        PEER_P2P_CLIENT     = 4
141        PEER_NAN            = 5
142
143    class RttPreamble(IntEnum):
144        PREAMBLE_LEGACY  = 0x01
145        PREAMBLE_HT      = 0x02
146        PREAMBLE_VHT     = 0x04
147
148    class RttBW(IntEnum):
149        BW_5_SUPPORT   = 0x01
150        BW_10_SUPPORT  = 0x02
151        BW_20_SUPPORT  = 0x04
152        BW_40_SUPPORT  = 0x08
153        BW_80_SUPPORT  = 0x10
154        BW_160_SUPPORT = 0x20
155
156    class Rtt(IntEnum):
157        STATUS_SUCCESS                  = 0
158        STATUS_FAILURE                  = 1
159        STATUS_FAIL_NO_RSP              = 2
160        STATUS_FAIL_REJECTED            = 3
161        STATUS_FAIL_NOT_SCHEDULED_YET   = 4
162        STATUS_FAIL_TM_TIMEOUT          = 5
163        STATUS_FAIL_AP_ON_DIFF_CHANNEL  = 6
164        STATUS_FAIL_NO_CAPABILITY       = 7
165        STATUS_ABORTED                  = 8
166        STATUS_FAIL_INVALID_TS          = 9
167        STATUS_FAIL_PROTOCOL            = 10
168        STATUS_FAIL_SCHEDULE            = 11
169        STATUS_FAIL_BUSY_TRY_LATER      = 12
170        STATUS_INVALID_REQ              = 13
171        STATUS_NO_WIFI                  = 14
172        STATUS_FAIL_FTM_PARAM_OVERRIDE  = 15
173
174        REASON_UNSPECIFIED              = -1
175        REASON_NOT_AVAILABLE            = -2
176        REASON_INVALID_LISTENER         = -3
177        REASON_INVALID_REQUEST          = -4
178
179    class RttParam:
180        device_type = "deviceType"
181        request_type = "requestType"
182        BSSID = "bssid"
183        channel_width = "channelWidth"
184        frequency = "frequency"
185        center_freq0 = "centerFreq0"
186        center_freq1 = "centerFreq1"
187        number_burst = "numberBurst"
188        interval = "interval"
189        num_samples_per_burst = "numSamplesPerBurst"
190        num_retries_per_measurement_frame = "numRetriesPerMeasurementFrame"
191        num_retries_per_FTMR = "numRetriesPerFTMR"
192        lci_request = "LCIRequest"
193        lcr_request = "LCRRequest"
194        burst_timeout = "burstTimeout"
195        preamble = "preamble"
196        bandwidth = "bandwidth"
197        margin = "margin"
198
199    RTT_MARGIN_OF_ERROR = {
200        RttBW.BW_80_SUPPORT: 2,
201        RttBW.BW_40_SUPPORT: 5,
202        RttBW.BW_20_SUPPORT: 5
203    }
204
205    # Macros as specified in the WifiScanner code.
206    WIFI_BAND_UNSPECIFIED = 0      # not specified
207    WIFI_BAND_24_GHZ = 1           # 2.4 GHz band
208    WIFI_BAND_5_GHZ = 2            # 5 GHz band without DFS channels
209    WIFI_BAND_5_GHZ_DFS_ONLY  = 4  # 5 GHz band with DFS channels
210    WIFI_BAND_5_GHZ_WITH_DFS  = 6  # 5 GHz band with DFS channels
211    WIFI_BAND_BOTH = 3             # both bands without DFS channels
212    WIFI_BAND_BOTH_WITH_DFS = 7    # both bands with DFS channels
213
214    REPORT_EVENT_AFTER_BUFFER_FULL = 0
215    REPORT_EVENT_AFTER_EACH_SCAN = 1
216    REPORT_EVENT_FULL_SCAN_RESULT = 2
217
218    # US Wifi frequencies
219    ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
220                          2457, 2462]
221    DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560, 5580,
222                          5600, 5620, 5640, 5660, 5680, 5700, 5720]
223    NONE_DFS_5G_FREQUENCIES = [5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805,
224                               5825]
225    ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
226
227    band_to_frequencies = {
228      WIFI_BAND_24_GHZ: ALL_2G_FREQUENCIES,
229      WIFI_BAND_5_GHZ: NONE_DFS_5G_FREQUENCIES,
230      WIFI_BAND_5_GHZ_DFS_ONLY: DFS_5G_FREQUENCIES,
231      WIFI_BAND_5_GHZ_WITH_DFS: ALL_5G_FREQUENCIES,
232      WIFI_BAND_BOTH: ALL_2G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES,
233      WIFI_BAND_BOTH_WITH_DFS: ALL_5G_FREQUENCIES + ALL_2G_FREQUENCIES
234    }
235
236    # All Wifi frequencies to channels lookup.
237    freq_to_channel = {
238        2412: 1,
239        2417: 2,
240        2422: 3,
241        2427: 4,
242        2432: 5,
243        2437: 6,
244        2442: 7,
245        2447: 8,
246        2452: 9,
247        2457: 10,
248        2462: 11,
249        2467: 12,
250        2472: 13,
251        2484: 14,
252        4915: 183,
253        4920: 184,
254        4925: 185,
255        4935: 187,
256        4940: 188,
257        4945: 189,
258        4960: 192,
259        4980: 196,
260        5035: 7,
261        5040: 8,
262        5045: 9,
263        5055: 11,
264        5060: 12,
265        5080: 16,
266        5170: 34,
267        5180: 36,
268        5190: 38,
269        5200: 40,
270        5210: 42,
271        5220: 44,
272        5230: 46,
273        5240: 48,
274        5260: 52,
275        5280: 56,
276        5300: 60,
277        5320: 64,
278        5500: 100,
279        5520: 104,
280        5540: 108,
281        5560: 112,
282        5580: 116,
283        5600: 120,
284        5620: 124,
285        5640: 128,
286        5660: 132,
287        5680: 136,
288        5700: 140,
289        5745: 149,
290        5765: 153,
291        5785: 157,
292        5805: 161,
293        5825: 165,
294    }
295
296    # All Wifi channels to frequencies lookup.
297    channel_2G_to_freq = {
298        1: 2412,
299        2: 2417,
300        3: 2422,
301        4: 2427,
302        5: 2432,
303        6: 2437,
304        7: 2442,
305        8: 2447,
306        9: 2452,
307        10: 2457,
308        11: 2462,
309        12: 2467,
310        13: 2472,
311        14: 2484
312    }
313
314    channel_5G_to_freq = {
315        183: 4915,
316        184: 4920,
317        185: 4925,
318        187: 4935,
319        188: 4940,
320        189: 4945,
321        192: 4960,
322        196: 4980,
323        7: 5035,
324        8: 5040,
325        9: 5045,
326        11: 5055,
327        12: 5060,
328        16: 5080,
329        34: 5170,
330        36: 5180,
331        38: 5190,
332        40: 5200,
333        42: 5210,
334        44: 5220,
335        46: 5230,
336        48: 5240,
337        52: 5260,
338        56: 5280,
339        60: 5300,
340        64: 5320,
341        100: 5500,
342        104: 5520,
343        108: 5540,
344        112: 5560,
345        116: 5580,
346        120: 5600,
347        124: 5620,
348        128: 5640,
349        132: 5660,
350        136: 5680,
351        140: 5700,
352        149: 5745,
353        153: 5765,
354        157: 5785,
355        161: 5805,
356        165: 5825
357    }
358
359class WifiEventNames:
360    WIFI_CONNECTED = "WifiNetworkConnected"
361    SUPPLICANT_CON_CHANGED = "SupplicantConnectionChanged"
362    WIFI_FORGET_NW_SUCCESS = "WifiManagerForgetNetworkOnSuccess"
363
364class WifiTestUtilsError(Exception):
365    pass
366
367class WifiChannelBase:
368    ALL_2G_FREQUENCIES = []
369    DFS_5G_FREQUENCIES = []
370    NONE_DFS_5G_FREQUENCIES = []
371    ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
372    MIX_CHANNEL_SCAN = []
373
374    def band_to_freq(self, band):
375        _band_to_frequencies = {
376            WifiEnums.WIFI_BAND_24_GHZ: self.ALL_2G_FREQUENCIES,
377            WifiEnums.WIFI_BAND_5_GHZ: self.NONE_DFS_5G_FREQUENCIES,
378            WifiEnums.WIFI_BAND_5_GHZ_DFS_ONLY: self.DFS_5G_FREQUENCIES,
379            WifiEnums.WIFI_BAND_5_GHZ_WITH_DFS: self.ALL_5G_FREQUENCIES,
380            WifiEnums.WIFI_BAND_BOTH: self.ALL_2G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES,
381            WifiEnums.WIFI_BAND_BOTH_WITH_DFS: self.ALL_5G_FREQUENCIES + self.ALL_2G_FREQUENCIES
382        }
383        return _band_to_frequencies[band]
384
385class WifiChannelUS(WifiChannelBase):
386    # US Wifi frequencies
387    ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
388                          2457, 2462]
389    NONE_DFS_5G_FREQUENCIES = [5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805,
390                               5825]
391    MIX_CHANNEL_SCAN = [2412, 2437, 2462, 5180, 5200, 5280, 5260, 5300,5500, 5320,
392                        5520, 5560, 5700, 5745, 5805]
393
394    def __init__(self, model=None):
395        if model and trim_model_name(model) in K_DEVICES:
396            self.DFS_5G_FREQUENCIES = []
397            self.ALL_5G_FREQUENCIES = self.NONE_DFS_5G_FREQUENCIES
398            self.MIX_CHANNEL_SCAN = [2412, 2437, 2462, 5180, 5200, 5240, 5745, 5765]
399        elif model and trim_model_name(model) in L_DEVICES:
400            self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520,
401                                       5540, 5560, 5580, 5660, 5680, 5700]
402            self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
403        elif model and trim_model_name(model) in L_TAP_DEVICES:
404            self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520,
405                                       5540, 5560, 5580, 5660, 5680, 5700, 5720]
406            self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
407        elif model and trim_model_name(model) in M_DEVICES:
408            self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560,5580,
409                                       5600, 5620, 5640, 5660, 5680, 5700]
410            self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
411        else:
412            self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560,5580,
413                                       5600, 5620, 5640, 5660, 5680, 5700, 5720]
414            self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
415
416def match_networks(target_params, networks):
417    """Finds the WiFi networks that match a given set of parameters in a list
418    of WiFi networks.
419
420    To be considered a match, a network needs to have all the target parameters
421    and the values of those parameters need to equal to those of the target
422    parameters.
423
424    Args:
425        target_params: The target parameters to match networks against.
426        networks: A list of dict objects representing WiFi networks.
427
428    Returns:
429        The networks that match the target parameters.
430    """
431    results = []
432    for n in networks:
433        for k, v in target_params.items():
434            if k not in n:
435                continue
436            if n[k] != v:
437                continue
438            results.append(n)
439    return results
440
441def wifi_toggle_state(ad, new_state=None):
442    """Toggles the state of wifi.
443
444    Args:
445        ad: An AndroidDevice object.
446        new_state: Wifi state to set to. If None, opposite of the current state.
447
448    Returns:
449        True if the toggle was successful, False otherwise.
450    """
451    # Check if the new_state is already achieved, so we don't wait for the
452    # state change event by mistake.
453    if new_state == ad.droid.wifiCheckState():
454        return True
455    ad.droid.wifiStartTrackingStateChange()
456    log.info("Setting wifi state to {}".format(new_state))
457    ad.droid.wifiToggleState(new_state)
458    try:
459        event = ad.ed.pop_event(WifiEventNames.SUPPLICANT_CON_CHANGED, SHORT_TIMEOUT)
460        return event['data']['Connected'] == new_state
461    except Empty:
462      # Supplicant connection event is not always reliable. We double check here
463      # and call it a success as long as the new state equals the expected state.
464        return new_state == ad.droid.wifiCheckState()
465    finally:
466        ad.droid.wifiStopTrackingStateChange()
467
468def reset_wifi(ad):
469    """Clears all saved networks on a device.
470
471    Args:
472        ad: An AndroidDevice object.
473
474    Raises:
475        WifiTestUtilsError is raised if forget network operation failed.
476    """
477    ad.droid.wifiToggleState(True)
478    networks = ad.droid.wifiGetConfiguredNetworks()
479    if not networks:
480        return
481    for n in networks:
482        ad.droid.wifiForgetNetwork(n['networkId'])
483        try:
484            event = ad.ed.pop_event(WifiEventNames.WIFI_FORGET_NW_SUCCESS,
485              SHORT_TIMEOUT)
486        except Empty:
487            raise WifiTestUtilsError("Failed to remove network {}.".format(n))
488
489def wifi_forget_network(ad, net_ssid):
490    """Remove configured Wifi network on an android device.
491
492    Args:
493        ad: android_device object for forget network.
494        net_ssid: ssid of network to be forget
495
496    Raises:
497        WifiTestUtilsError is raised if forget network operation failed.
498    """
499    droid, ed = ad.droid, ad.ed
500    droid.wifiToggleState(True)
501    networks = droid.wifiGetConfiguredNetworks()
502    if not networks:
503        return
504    for n in networks:
505        if net_ssid in n[WifiEnums.SSID_KEY]:
506            droid.wifiForgetNetwork(n['networkId'])
507            try:
508                event = ed.pop_event(WifiEventNames.WIFI_FORGET_NW_SUCCESS,
509                        SHORT_TIMEOUT)
510            except Empty:
511                raise WifiTestUtilsError("Failed to remove network %s." % n)
512
513def wifi_test_device_init(ad):
514    """Initializes an android device for wifi testing.
515
516    0. Make sure SL4A connection is established on the android device.
517    1. Disable location service's WiFi scan.
518    2. Turn WiFi on.
519    3. Clear all saved networks.
520    4. Set country code to US.
521    5. Enable WiFi verbose logging.
522    6. Sync device time with computer time.
523    7. Turn off cellular data.
524    """
525    require_sl4a((ad,))
526    ad.droid.wifiScannerToggleAlwaysAvailable(False)
527    msg = "Failed to turn off location service's scan."
528    assert not ad.droid.wifiScannerIsAlwaysAvailable(), msg
529    msg = "Failed to turn WiFi on %s" % ad.serial
530    assert wifi_toggle_state(ad, True), msg
531    reset_wifi(ad)
532    msg = "Failed to clear configured networks."
533    assert not ad.droid.wifiGetConfiguredNetworks(), msg
534    ad.droid.wifiEnableVerboseLogging(1)
535    msg = "Failed to enable WiFi verbose logging."
536    assert ad.droid.wifiGetVerboseLoggingLevel() == 1, msg
537    ad.droid.wifiScannerToggleAlwaysAvailable(False)
538    # We don't verify the following settings since they are not critical.
539    sync_device_time(ad)
540    ad.droid.telephonyToggleDataConnection(False)
541    # TODO(angli): need to verify the country code was actually set. No generic
542    # way to check right now.
543    ad.adb.shell("halutil -country %s" % WifiEnums.CountryCode.US)
544
545def sort_wifi_scan_results(results, key="level"):
546    """Sort wifi scan results by key.
547
548    Args:
549        results: A list of results to sort.
550        key: Name of the field to sort the results by.
551
552    Returns:
553        A list of results in sorted order.
554    """
555    return sorted(results, lambda d: (key not in d, d[key]))
556
557def start_wifi_connection_scan(ad):
558    """Starts a wifi connection scan and wait for results to become available.
559
560    Args:
561        ad: An AndroidDevice object.
562    """
563    ad.droid.wifiStartScan()
564    ad.ed.pop_event("WifiManagerScanResultsAvailable", 60)
565
566def start_wifi_background_scan(ad, scan_setting):
567    """Starts wifi background scan.
568
569    Args:
570        ad: android_device object to initiate connection on.
571        scan_setting: A dict representing the settings of the scan.
572
573    Returns:
574        If scan was started successfully, event data of success event is returned.
575    """
576    droid, ed = ad.droids[0], ad.eds[0]
577    idx = droid.wifiScannerStartBackgroundScan(scan_setting)
578    event = ed.pop_event("WifiScannerScan{}onSuccess".format(idx),
579                         SHORT_TIMEOUT)
580    return event['data']
581
582def start_wifi_tethering(ad, ssid, password, band=None):
583    """Starts wifi tethering on an android_device.
584
585    Args:
586        ad: android_device to start wifi tethering on.
587        ssid: The SSID the soft AP should broadcast.
588        password: The password the soft AP should use.
589        band: The band the soft AP should be set on. It should be either
590            WifiEnums.WIFI_CONFIG_APBAND_2G or WifiEnums.WIFI_CONFIG_APBAND_5G.
591
592    Returns:
593        True if soft AP was started successfully, False otherwise.
594    """
595    droid, ed = ad.droid, ad.ed
596    droid.wifiStartTrackingStateChange()
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.wifiSetApEnabled(True, config):
605        return False
606    ed.pop_event("WifiManagerApEnabled", 30)
607    ed.wait_for_event("TetherStateChanged",
608        lambda x : x["data"]["ACTIVE_TETHER"], 30)
609    droid.wifiStopTrackingStateChange()
610    return True
611
612def stop_wifi_tethering(ad):
613    """Stops wifi tethering on an android_device.
614
615    Args:
616        ad: android_device to stop wifi tethering on.
617    """
618    droid, ed = ad.droid, ad.ed
619    droid.wifiStartTrackingStateChange()
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.wifiStopTrackingStateChange()
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