• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3#   Copyright 2017 - Google
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 base64
18import json
19import queue
20import re
21import statistics
22import time
23from acts import asserts
24
25from acts_contrib.test_utils.net import connectivity_const as cconsts
26from acts_contrib.test_utils.net import socket_test_utils as sutils
27from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
28
29# arbitrary timeout for events
30EVENT_TIMEOUT = 10
31
32# semi-arbitrary timeout for network formation events. Based on framework
33# timeout for NDP (NAN data-path) negotiation to be completed.
34EVENT_NDP_TIMEOUT = 20
35
36# number of second to 'reasonably' wait to make sure that devices synchronize
37# with each other - useful for OOB test cases, where the OOB discovery would
38# take some time
39WAIT_FOR_CLUSTER = 5
40
41
42def decorate_event(event_name, id):
43    return '%s_%d' % (event_name, id)
44
45
46def wait_for_event(ad, event_name, timeout=EVENT_TIMEOUT):
47    """Wait for the specified event or timeout.
48
49  Args:
50    ad: The android device
51    event_name: The event to wait on
52    timeout: Number of seconds to wait
53  Returns:
54    The event (if available)
55  """
56    prefix = ''
57    if hasattr(ad, 'pretty_name'):
58        prefix = '[%s] ' % ad.pretty_name
59    try:
60        event = ad.ed.pop_event(event_name, timeout)
61        ad.log.info('%s%s: %s', prefix, event_name, event['data'])
62        return event
63    except queue.Empty:
64        ad.log.info('%sTimed out while waiting for %s', prefix, event_name)
65        asserts.fail(event_name)
66
67def _filter_callbacks(event, expected_kv):
68    """
69    Helper method to use in |fail_on_event_with_keys| and
70    |wait_for_event_with_keys|
71    """
72    for expected_k, expected_v in expected_kv:
73        actual_v = event['data'][expected_k]
74        if isinstance(expected_v, dict) and isinstance(actual_v, dict):
75            # |expected_v| not a subset of |actual_v|
76            if not(expected_v.items() <= actual_v.items()):
77                return False
78        else:
79            if actual_v != expected_v:
80                return False
81    return True
82
83
84def wait_for_event_with_keys(ad,
85                             event_name,
86                             timeout=EVENT_TIMEOUT,
87                             *keyvalues):
88    """Wait for the specified event contain the key/value pairs or timeout
89
90  Args:
91    ad: The android device
92    event_name: The event to wait on
93    timeout: Number of seconds to wait
94    keyvalues: Expected (key, value) pairs. If the value for a key is a dict,
95               then this will perform subset matching for that key.
96  Returns:
97    The event (if available)
98  """
99    prefix = ''
100    if hasattr(ad, 'pretty_name'):
101        prefix = '[%s] ' % ad.pretty_name
102    try:
103        event = ad.ed.wait_for_event(event_name, _filter_callbacks, timeout,
104                                     keyvalues)
105        ad.log.info('%s%s: %s', prefix, event_name, event['data'])
106        return event
107    except queue.Empty:
108        ad.log.info('%sTimed out while waiting for %s (%s)', prefix,
109                    event_name, keyvalues)
110        asserts.fail(event_name)
111
112
113def fail_on_event(ad, event_name, timeout=EVENT_TIMEOUT):
114    """Wait for a timeout period and looks for the specified event - fails if it
115  is observed.
116
117  Args:
118    ad: The android device
119    event_name: The event to wait for (and fail on its appearance)
120  """
121    prefix = ''
122    if hasattr(ad, 'pretty_name'):
123        prefix = '[%s] ' % ad.pretty_name
124    try:
125        event = ad.ed.pop_event(event_name, timeout)
126        ad.log.info('%sReceived unwanted %s: %s', prefix, event_name,
127                    event['data'])
128        asserts.fail(event_name, extras=event)
129    except queue.Empty:
130        ad.log.info('%s%s not seen (as expected)', prefix, event_name)
131        return
132
133
134def fail_on_event_with_keys(ad, event_name, timeout=EVENT_TIMEOUT, *keyvalues):
135    """Wait for a timeout period and looks for the specified event which contains
136  the key/value pairs - fails if it is observed.
137
138  Args:
139    ad: The android device
140    event_name: The event to wait on
141    timeout: Number of seconds to wait
142    keyvalues: Expected (key, value) pairs. If the value for a key is a dict,
143               then this will perform subset matching for that key.
144  """
145    prefix = ''
146    if hasattr(ad, 'pretty_name'):
147        prefix = '[%s] ' % ad.pretty_name
148    try:
149        event = ad.ed.wait_for_event(event_name, _filter_callbacks, timeout,
150                                     keyvalues)
151        ad.log.info('%sReceived unwanted %s: %s', prefix, event_name,
152                    event['data'])
153        asserts.fail(event_name, extras=event)
154    except queue.Empty:
155        ad.log.info('%s%s (%s) not seen (as expected)', prefix, event_name,
156                    keyvalues)
157        return
158
159
160def verify_no_more_events(ad, timeout=EVENT_TIMEOUT):
161    """Verify that there are no more events in the queue.
162  """
163    prefix = ''
164    if hasattr(ad, 'pretty_name'):
165        prefix = '[%s] ' % ad.pretty_name
166    should_fail = False
167    try:
168        while True:
169            event = ad.ed.pop_events('.*', timeout, freq=0)
170            ad.log.info('%sQueue contains %s', prefix, event)
171            should_fail = True
172    except queue.Empty:
173        if should_fail:
174            asserts.fail('%sEvent queue not empty' % prefix)
175        ad.log.info('%sNo events in the queue (as expected)', prefix)
176        return
177
178
179def encode_list(list_of_objects):
180    """Converts the list of strings or bytearrays to a list of b64 encoded
181  bytearrays.
182
183  A None object is treated as a zero-length bytearray.
184
185  Args:
186    list_of_objects: A list of strings or bytearray objects
187  Returns: A list of the same objects, converted to bytes and b64 encoded.
188  """
189    encoded_list = []
190    for obj in list_of_objects:
191        if obj is None:
192            obj = bytes()
193        if isinstance(obj, str):
194            encoded_list.append(
195                base64.b64encode(bytes(obj, 'utf-8')).decode('utf-8'))
196        else:
197            encoded_list.append(base64.b64encode(obj).decode('utf-8'))
198    return encoded_list
199
200
201def decode_list(list_of_b64_strings):
202    """Converts the list of b64 encoded strings to a list of bytearray.
203
204  Args:
205    list_of_b64_strings: list of strings, each of which is b64 encoded array
206  Returns: a list of bytearrays.
207  """
208    decoded_list = []
209    for str in list_of_b64_strings:
210        decoded_list.append(base64.b64decode(str))
211    return decoded_list
212
213
214def construct_max_match_filter(max_size):
215    """Constructs a maximum size match filter that fits into the 'max_size' bytes.
216
217  Match filters are a set of LVs (Length, Value pairs) where L is 1 byte. The
218  maximum size match filter will contain max_size/2 LVs with all Vs (except
219  possibly the last one) of 1 byte, the last V may be 2 bytes for odd max_size.
220
221  Args:
222    max_size: Maximum size of the match filter.
223  Returns: an array of bytearrays.
224  """
225    mf_list = []
226    num_lvs = max_size // 2
227    for i in range(num_lvs - 1):
228        mf_list.append(bytes([i]))
229    if (max_size % 2 == 0):
230        mf_list.append(bytes([255]))
231    else:
232        mf_list.append(bytes([254, 255]))
233    return mf_list
234
235
236def assert_equal_strings(first, second, msg=None, extras=None):
237    """Assert equality of the string operands - where None is treated as equal to
238  an empty string (''), otherwise fail the test.
239
240  Error message is "first != second" by default. Additional explanation can
241  be supplied in the message.
242
243  Args:
244      first, seconds: The strings that are evaluated for equality.
245      msg: A string that adds additional info about the failure.
246      extras: An optional field for extra information to be included in
247              test result.
248  """
249    if first == None:
250        first = ''
251    if second == None:
252        second = ''
253    asserts.assert_equal(first, second, msg, extras)
254
255
256def get_aware_capabilities(ad):
257    """Get the Wi-Fi Aware capabilities from the specified device. The
258  capabilities are a dictionary keyed by aware_const.CAP_* keys.
259
260  Args:
261    ad: the Android device
262  Returns: the capability dictionary.
263  """
264    return json.loads(ad.adb.shell('cmd wifiaware state_mgr get_capabilities'))
265
266
267def get_wifi_mac_address(ad):
268    """Get the Wi-Fi interface MAC address as a upper-case string of hex digits
269  without any separators (e.g. ':').
270
271  Args:
272    ad: Device on which to run.
273  """
274    return ad.droid.wifiGetConnectionInfo()['mac_address'].upper().replace(
275        ':', '')
276
277
278def validate_forbidden_callbacks(ad, limited_cb=None):
279    """Validate that the specified callbacks have not been called more then permitted.
280
281  In addition to the input configuration also validates that forbidden callbacks
282  have never been called.
283
284  Args:
285    ad: Device on which to run.
286    limited_cb: Dictionary of CB_EV_* ids and maximum permitted calls (0
287                meaning never).
288  """
289    cb_data = json.loads(ad.adb.shell('cmd wifiaware native_cb get_cb_count'))
290
291    if limited_cb is None:
292        limited_cb = {}
293
294    fail = False
295    for cb_event in limited_cb.keys():
296        if cb_event in cb_data:
297            if cb_data[cb_event] > limited_cb[cb_event]:
298                fail = True
299                ad.log.info(
300                    'Callback %s observed %d times: more then permitted %d times',
301                    cb_event, cb_data[cb_event], limited_cb[cb_event])
302
303    asserts.assert_false(fail, 'Forbidden callbacks observed', extras=cb_data)
304
305
306def extract_stats(ad, data, results, key_prefix, log_prefix):
307    """Extract statistics from the data, store in the results dictionary, and
308  output to the info log.
309
310  Args:
311    ad: Android device (for logging)
312    data: A list containing the data to be analyzed.
313    results: A dictionary into which to place the statistics.
314    key_prefix: A string prefix to use for the dict keys storing the
315                extracted stats.
316    log_prefix: A string prefix to use for the info log.
317    include_data: If True includes the raw data in the dictionary,
318                  otherwise just the stats.
319  """
320    num_samples = len(data)
321    results['%snum_samples' % key_prefix] = num_samples
322
323    if not data:
324        return
325
326    data_min = min(data)
327    data_max = max(data)
328    data_mean = statistics.mean(data)
329    data_cdf = extract_cdf(data)
330    data_cdf_decile = extract_cdf_decile(data_cdf)
331
332    results['%smin' % key_prefix] = data_min
333    results['%smax' % key_prefix] = data_max
334    results['%smean' % key_prefix] = data_mean
335    results['%scdf' % key_prefix] = data_cdf
336    results['%scdf_decile' % key_prefix] = data_cdf_decile
337    results['%sraw_data' % key_prefix] = data
338
339    if num_samples > 1:
340        data_stdev = statistics.stdev(data)
341        results['%sstdev' % key_prefix] = data_stdev
342        ad.log.info(
343            '%s: num_samples=%d, min=%.2f, max=%.2f, mean=%.2f, stdev=%.2f, cdf_decile=%s',
344            log_prefix, num_samples, data_min, data_max, data_mean, data_stdev,
345            data_cdf_decile)
346    else:
347        ad.log.info(
348            '%s: num_samples=%d, min=%.2f, max=%.2f, mean=%.2f, cdf_decile=%s',
349            log_prefix, num_samples, data_min, data_max, data_mean,
350            data_cdf_decile)
351
352
353def extract_cdf_decile(cdf):
354    """Extracts the 10%, 20%, ..., 90% points from the CDF and returns their
355  value (a list of 9 values).
356
357  Since CDF may not (will not) have exact x% value picks the value >= x%.
358
359  Args:
360    cdf: a list of 2 lists, the X and Y of the CDF.
361  """
362    decades = []
363    next_decade = 10
364    for x, y in zip(cdf[0], cdf[1]):
365        while 100 * y >= next_decade:
366            decades.append(x)
367            next_decade = next_decade + 10
368        if next_decade == 100:
369            break
370    return decades
371
372
373def extract_cdf(data):
374    """Calculates the Cumulative Distribution Function (CDF) of the data.
375
376  Args:
377      data: A list containing data (does not have to be sorted).
378
379  Returns: a list of 2 lists: the X and Y axis of the CDF.
380  """
381    x = []
382    cdf = []
383    if not data:
384        return (x, cdf)
385
386    all_values = sorted(data)
387    for val in all_values:
388        if not x:
389            x.append(val)
390            cdf.append(1)
391        else:
392            if x[-1] == val:
393                cdf[-1] += 1
394            else:
395                x.append(val)
396                cdf.append(cdf[-1] + 1)
397
398    scale = 1.0 / len(all_values)
399    for i in range(len(cdf)):
400        cdf[i] = cdf[i] * scale
401
402    return (x, cdf)
403
404
405def get_mac_addr(device, interface):
406    """Get the MAC address of the specified interface. Uses ifconfig and parses
407  its output. Normalizes string to remove ':' and upper case.
408
409  Args:
410    device: Device on which to query the interface MAC address.
411    interface: Name of the interface for which to obtain the MAC address.
412  """
413    out = device.adb.shell("ifconfig %s" % interface)
414    res = re.match(".* HWaddr (\S+).*", out, re.S)
415    asserts.assert_true(res,
416                        'Unable to obtain MAC address for interface %s' %
417                        interface,
418                        extras=out)
419    return res.group(1).upper().replace(':', '')
420
421
422def get_ipv6_addr(device, interface):
423    """Get the IPv6 address of the specified interface. Uses ifconfig and parses
424  its output. Returns a None if the interface does not have an IPv6 address
425  (indicating it is not UP).
426
427  Args:
428    device: Device on which to query the interface IPv6 address.
429    interface: Name of the interface for which to obtain the IPv6 address.
430  """
431    out = device.adb.shell("ifconfig %s" % interface)
432    res = re.match(".*inet6 addr: (\S+)/.*", out, re.S)
433    if not res:
434        return None
435    return res.group(1)
436
437
438def verify_socket_connect(dut_s, dut_c, ipv6_s, ipv6_c, port):
439    """Verify the socket connection between server (dut_s) and client (dut_c)
440    using the given IPv6 addresses.
441
442    Opens a ServerSocket on the server and tries to connect to it
443    from the client.
444
445    Args:
446        dut_s, dut_c: the server and client devices under test (DUTs)
447        ipv6_s, ipv6_c: the scoped link-local addresses of the server and client.
448        port: the port to use
449    Return: True on success, False otherwise
450    """
451    server_sock = None
452    sock_c = None
453    sock_s = None
454    try:
455        server_sock = sutils.open_server_socket(dut_s, ipv6_s, port)
456        port_to_use = port
457        if port == 0:
458            port_to_use = dut_s.droid.getTcpServerSocketPort(server_sock)
459        sock_c, sock_s = sutils.open_connect_socket(dut_c, dut_s, ipv6_c,
460                                                    ipv6_s, 0, port_to_use,
461                                                    server_sock)
462    except:
463        return False
464    finally:
465        if sock_c is not None:
466            sutils.close_socket(dut_c, sock_c)
467        if sock_s is not None:
468            sutils.close_socket(dut_s, sock_s)
469        if server_sock is not None:
470            sutils.close_server_socket(dut_s, server_sock)
471    return True
472
473
474def run_ping6(dut, target_ip, duration=60):
475    """Run ping test and return the latency result
476
477    Args:
478        dut: the dut which run the ping cmd
479        target_ip: target IP Address for ping
480        duration: the duration time of the ping
481
482    return: dict contains "min/avg/max/mdev" result
483    """
484    cmd = "ping6 -w %d %s" % (duration, target_ip)
485    ping_result = dut.adb.shell(cmd, timeout=duration + 1)
486    res = re.match(".*mdev = (\S+) .*", ping_result, re.S)
487    asserts.assert_true(res, "Cannot reach the IP address %s", target_ip)
488    title = ["min", "avg", "max", "mdev"]
489    result = res.group(1).split("/")
490    latency_result = {}
491    for i in range(len(title)):
492        latency_result[title[i]] = result[i]
493    return latency_result
494
495
496def reset_device_parameters(ad):
497    """Reset device configurations.
498
499    Args:
500      ad: device to be reset
501    """
502    ad.adb.shell("cmd wifiaware reset")
503
504
505def reset_device_statistics(ad):
506    """Reset device statistics.
507
508    Args:
509        ad: device to be reset
510    """
511    ad.adb.shell("cmd wifiaware native_cb get_cb_count --reset")
512
513
514def set_power_mode_parameters(ad, power_mode):
515    """Set device power mode.
516
517    Set the power configuration DW parameters for the device based on any
518    configuration overrides (if provided)
519
520    Args:
521        ad: android device
522        power_mode: Desired power mode (INTERACTIVE or NON_INTERACTIVE)
523    """
524    if power_mode == "INTERACTIVE":
525        config_settings_high_power(ad)
526    elif power_mode == "NON_INTERACTIVE":
527        config_settings_low_power(ad)
528    else:
529        asserts.assert_false(
530            "The 'aware_default_power_mode' configuration must be INTERACTIVE or "
531            "NON_INTERACTIVE")
532
533
534#########################################################
535# Aware primitives
536#########################################################
537
538
539def request_network(dut, ns):
540    """Request a Wi-Fi Aware network.
541
542  Args:
543    dut: Device
544    ns: Network specifier
545  Returns: the request key
546  """
547    network_req = {"TransportType": 5, "NetworkSpecifier": ns}
548    return dut.droid.connectivityRequestWifiAwareNetwork(network_req)
549
550
551def get_network_specifier(dut, id, dev_type, peer_mac, sec):
552    """Create a network specifier for the device based on the security
553  configuration.
554
555  Args:
556    dut: device
557    id: session ID
558    dev_type: device type - Initiator or Responder
559    peer_mac: the discovery MAC address of the peer
560    sec: security configuration
561  """
562    if sec is None:
563        return dut.droid.wifiAwareCreateNetworkSpecifierOob(
564            id, dev_type, peer_mac)
565    if isinstance(sec, str):
566        return dut.droid.wifiAwareCreateNetworkSpecifierOob(
567            id, dev_type, peer_mac, sec)
568    return dut.droid.wifiAwareCreateNetworkSpecifierOob(
569        id, dev_type, peer_mac, None, sec)
570
571
572def configure_power_setting(device, mode, name, value):
573    """Use the command-line API to configure the power setting
574
575  Args:
576    device: Device on which to perform configuration
577    mode: The power mode being set, should be "default", "inactive", or "idle"
578    name: One of the power settings from 'wifiaware set-power'.
579    value: An integer.
580  """
581    device.adb.shell("cmd wifiaware native_api set-power %s %s %d" %
582                     (mode, name, value))
583
584
585def configure_mac_random_interval(device, interval_sec):
586    """Use the command-line API to configure the MAC address randomization
587  interval.
588
589  Args:
590    device: Device on which to perform configuration
591    interval_sec: The MAC randomization interval in seconds. A value of 0
592                  disables all randomization.
593  """
594    device.adb.shell(
595        "cmd wifiaware native_api set mac_random_interval_sec %d" %
596        interval_sec)
597
598
599def configure_ndp_allow_any_override(device, override_api_check):
600    """Use the command-line API to configure whether an NDP Responder may be
601  configured to accept an NDP request from ANY peer.
602
603  By default the target API level of the requesting app determines whether such
604  configuration is permitted. This allows overriding the API check and allowing
605  it.
606
607  Args:
608    device: Device on which to perform configuration.
609    override_api_check: True to allow a Responder to ANY configuration, False to
610                        perform the API level check.
611  """
612    device.adb.shell("cmd wifiaware state_mgr allow_ndp_any %s" %
613                     ("true" if override_api_check else "false"))
614
615
616def config_settings_high_power(device):
617    """Configure device's power settings values to high power mode -
618  whether device is in interactive or non-interactive modes"""
619    configure_power_setting(device, "default", "dw_24ghz",
620                            aconsts.POWER_DW_24_INTERACTIVE)
621    configure_power_setting(device, "default", "dw_5ghz",
622                            aconsts.POWER_DW_5_INTERACTIVE)
623    configure_power_setting(device, "default", "disc_beacon_interval_ms",
624                            aconsts.POWER_DISC_BEACON_INTERVAL_INTERACTIVE)
625    configure_power_setting(device, "default", "num_ss_in_discovery",
626                            aconsts.POWER_NUM_SS_IN_DISC_INTERACTIVE)
627    configure_power_setting(device, "default", "enable_dw_early_term",
628                            aconsts.POWER_ENABLE_DW_EARLY_TERM_INTERACTIVE)
629
630    configure_power_setting(device, "inactive", "dw_24ghz",
631                            aconsts.POWER_DW_24_INTERACTIVE)
632    configure_power_setting(device, "inactive", "dw_5ghz",
633                            aconsts.POWER_DW_5_INTERACTIVE)
634    configure_power_setting(device, "inactive", "disc_beacon_interval_ms",
635                            aconsts.POWER_DISC_BEACON_INTERVAL_INTERACTIVE)
636    configure_power_setting(device, "inactive", "num_ss_in_discovery",
637                            aconsts.POWER_NUM_SS_IN_DISC_INTERACTIVE)
638    configure_power_setting(device, "inactive", "enable_dw_early_term",
639                            aconsts.POWER_ENABLE_DW_EARLY_TERM_INTERACTIVE)
640
641
642def config_settings_low_power(device):
643    """Configure device's power settings values to low power mode - whether
644  device is in interactive or non-interactive modes"""
645    configure_power_setting(device, "default", "dw_24ghz",
646                            aconsts.POWER_DW_24_NON_INTERACTIVE)
647    configure_power_setting(device, "default", "dw_5ghz",
648                            aconsts.POWER_DW_5_NON_INTERACTIVE)
649    configure_power_setting(device, "default", "disc_beacon_interval_ms",
650                            aconsts.POWER_DISC_BEACON_INTERVAL_NON_INTERACTIVE)
651    configure_power_setting(device, "default", "num_ss_in_discovery",
652                            aconsts.POWER_NUM_SS_IN_DISC_NON_INTERACTIVE)
653    configure_power_setting(device, "default", "enable_dw_early_term",
654                            aconsts.POWER_ENABLE_DW_EARLY_TERM_NON_INTERACTIVE)
655
656    configure_power_setting(device, "inactive", "dw_24ghz",
657                            aconsts.POWER_DW_24_NON_INTERACTIVE)
658    configure_power_setting(device, "inactive", "dw_5ghz",
659                            aconsts.POWER_DW_5_NON_INTERACTIVE)
660    configure_power_setting(device, "inactive", "disc_beacon_interval_ms",
661                            aconsts.POWER_DISC_BEACON_INTERVAL_NON_INTERACTIVE)
662    configure_power_setting(device, "inactive", "num_ss_in_discovery",
663                            aconsts.POWER_NUM_SS_IN_DISC_NON_INTERACTIVE)
664    configure_power_setting(device, "inactive", "enable_dw_early_term",
665                            aconsts.POWER_ENABLE_DW_EARLY_TERM_NON_INTERACTIVE)
666
667
668def config_power_settings(device,
669                          dw_24ghz,
670                          dw_5ghz,
671                          disc_beacon_interval=None,
672                          num_ss_in_disc=None,
673                          enable_dw_early_term=None):
674    """Configure device's discovery window (DW) values to the specified values -
675  whether the device is in interactive or non-interactive mode.
676
677  Args:
678    dw_24ghz: DW interval in the 2.4GHz band.
679    dw_5ghz: DW interval in the 5GHz band.
680    disc_beacon_interval: The discovery beacon interval (in ms). If None then
681                          not set.
682    num_ss_in_disc: Number of spatial streams to use for discovery. If None then
683                    not set.
684    enable_dw_early_term: If True then enable early termination of the DW. If
685                          None then not set.
686  """
687    configure_power_setting(device, "default", "dw_24ghz", dw_24ghz)
688    configure_power_setting(device, "default", "dw_5ghz", dw_5ghz)
689    configure_power_setting(device, "inactive", "dw_24ghz", dw_24ghz)
690    configure_power_setting(device, "inactive", "dw_5ghz", dw_5ghz)
691
692    if disc_beacon_interval is not None:
693        configure_power_setting(device, "default", "disc_beacon_interval_ms",
694                                disc_beacon_interval)
695        configure_power_setting(device, "inactive", "disc_beacon_interval_ms",
696                                disc_beacon_interval)
697
698    if num_ss_in_disc is not None:
699        configure_power_setting(device, "default", "num_ss_in_discovery",
700                                num_ss_in_disc)
701        configure_power_setting(device, "inactive", "num_ss_in_discovery",
702                                num_ss_in_disc)
703
704    if enable_dw_early_term is not None:
705        configure_power_setting(device, "default", "enable_dw_early_term",
706                                enable_dw_early_term)
707        configure_power_setting(device, "inactive", "enable_dw_early_term",
708                                enable_dw_early_term)
709
710
711def create_discovery_config(service_name,
712                            d_type,
713                            ssi=None,
714                            match_filter=None,
715                            match_filter_list=None,
716                            ttl=0,
717                            term_cb_enable=True):
718    """Create a publish discovery configuration based on input parameters.
719
720  Args:
721    service_name: Service name - required
722    d_type: Discovery type (publish or subscribe constants)
723    ssi: Supplemental information - defaults to None
724    match_filter, match_filter_list: The match_filter, only one mechanism can
725                                     be used to specify. Defaults to None.
726    ttl: Time-to-live - defaults to 0 (i.e. non-self terminating)
727    term_cb_enable: True (default) to enable callback on termination, False
728                    means that no callback is called when session terminates.
729  Returns:
730    publish discovery configuration object.
731  """
732    config = {}
733    config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = service_name
734    config[aconsts.DISCOVERY_KEY_DISCOVERY_TYPE] = d_type
735    if ssi is not None:
736        config[aconsts.DISCOVERY_KEY_SSI] = ssi
737    if match_filter is not None:
738        config[aconsts.DISCOVERY_KEY_MATCH_FILTER] = match_filter
739    if match_filter_list is not None:
740        config[aconsts.DISCOVERY_KEY_MATCH_FILTER_LIST] = match_filter_list
741    config[aconsts.DISCOVERY_KEY_TTL] = ttl
742    config[aconsts.DISCOVERY_KEY_TERM_CB_ENABLED] = term_cb_enable
743    return config
744
745
746def add_ranging_to_pub(p_config, enable_ranging):
747    """Add ranging enabled configuration to a publish configuration (only relevant
748  for publish configuration).
749
750  Args:
751    p_config: The Publish discovery configuration.
752    enable_ranging: True to enable ranging, False to disable.
753  Returns:
754    The modified publish configuration.
755  """
756    p_config[aconsts.DISCOVERY_KEY_RANGING_ENABLED] = enable_ranging
757    return p_config
758
759
760def add_ranging_to_sub(s_config, min_distance_mm, max_distance_mm):
761    """Add ranging distance configuration to a subscribe configuration (only
762  relevant to a subscribe configuration).
763
764  Args:
765    s_config: The Subscribe discovery configuration.
766    min_distance_mm, max_distance_mm: The min and max distance specification.
767                                      Used if not None.
768  Returns:
769    The modified subscribe configuration.
770  """
771    if min_distance_mm is not None:
772        s_config[aconsts.DISCOVERY_KEY_MIN_DISTANCE_MM] = min_distance_mm
773    if max_distance_mm is not None:
774        s_config[aconsts.DISCOVERY_KEY_MAX_DISTANCE_MM] = max_distance_mm
775    return s_config
776
777
778def attach_with_identity(dut):
779    """Start an Aware session (attach) and wait for confirmation and identity
780  information (mac address).
781
782  Args:
783    dut: Device under test
784  Returns:
785    id: Aware session ID.
786    mac: Discovery MAC address of this device.
787  """
788    id = dut.droid.wifiAwareAttach(True)
789    wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
790    event = wait_for_event(dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
791    mac = event["data"]["mac"]
792
793    return id, mac
794
795
796def create_discovery_pair(p_dut,
797                          s_dut,
798                          p_config,
799                          s_config,
800                          device_startup_offset,
801                          msg_id=None):
802    """Creates a discovery session (publish and subscribe), and waits for
803  service discovery - at that point the sessions are connected and ready for
804  further messaging of data-path setup.
805
806  Args:
807    p_dut: Device to use as publisher.
808    s_dut: Device to use as subscriber.
809    p_config: Publish configuration.
810    s_config: Subscribe configuration.
811    device_startup_offset: Number of seconds to offset the enabling of NAN on
812                           the two devices.
813    msg_id: Controls whether a message is sent from Subscriber to Publisher
814            (so that publisher has the sub's peer ID). If None then not sent,
815            otherwise should be an int for the message id.
816  Returns: variable size list of:
817    p_id: Publisher attach session id
818    s_id: Subscriber attach session id
819    p_disc_id: Publisher discovery session id
820    s_disc_id: Subscriber discovery session id
821    peer_id_on_sub: Peer ID of the Publisher as seen on the Subscriber
822    peer_id_on_pub: Peer ID of the Subscriber as seen on the Publisher. Only
823                    included if |msg_id| is not None.
824  """
825    p_dut.pretty_name = 'Publisher'
826    s_dut.pretty_name = 'Subscriber'
827
828    # Publisher+Subscriber: attach and wait for confirmation
829    p_id = p_dut.droid.wifiAwareAttach()
830    wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
831    time.sleep(device_startup_offset)
832    s_id = s_dut.droid.wifiAwareAttach()
833    wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
834
835    # Publisher: start publish and wait for confirmation
836    p_disc_id = p_dut.droid.wifiAwarePublish(p_id, p_config)
837    wait_for_event(p_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
838
839    # Subscriber: start subscribe and wait for confirmation
840    s_disc_id = s_dut.droid.wifiAwareSubscribe(s_id, s_config)
841    wait_for_event(s_dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
842
843    # Subscriber: wait for service discovery
844    discovery_event = wait_for_event(s_dut,
845                                     aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
846    peer_id_on_sub = discovery_event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
847
848    # Optionally send a message from Subscriber to Publisher
849    if msg_id is not None:
850        ping_msg = 'PING'
851
852        # Subscriber: send message to peer (Publisher)
853        s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub, msg_id,
854                                         ping_msg, aconsts.MAX_TX_RETRIES)
855        sub_tx_msg_event = wait_for_event(s_dut,
856                                          aconsts.SESSION_CB_ON_MESSAGE_SENT)
857        asserts.assert_equal(
858            msg_id,
859            sub_tx_msg_event['data'][aconsts.SESSION_CB_KEY_MESSAGE_ID],
860            'Subscriber -> Publisher message ID corrupted')
861
862        # Publisher: wait for received message
863        pub_rx_msg_event = wait_for_event(
864            p_dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED)
865        peer_id_on_pub = pub_rx_msg_event['data'][
866            aconsts.SESSION_CB_KEY_PEER_ID]
867        asserts.assert_equal(
868            ping_msg,
869            pub_rx_msg_event['data'][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING],
870            'Subscriber -> Publisher message corrupted')
871        return p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub, peer_id_on_pub
872
873    return p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub
874
875
876def create_ib_ndp(p_dut, s_dut, p_config, s_config, device_startup_offset):
877    """Create an NDP (using in-band discovery)
878
879  Args:
880    p_dut: Device to use as publisher.
881    s_dut: Device to use as subscriber.
882    p_config: Publish configuration.
883    s_config: Subscribe configuration.
884    device_startup_offset: Number of seconds to offset the enabling of NAN on
885                           the two devices.
886  """
887    (p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub,
888     peer_id_on_pub) = create_discovery_pair(p_dut,
889                                             s_dut,
890                                             p_config,
891                                             s_config,
892                                             device_startup_offset,
893                                             msg_id=9999)
894
895    # Publisher: request network
896    p_req_key = request_network(
897        p_dut,
898        p_dut.droid.wifiAwareCreateNetworkSpecifier(p_disc_id, peer_id_on_pub,
899                                                    None))
900
901    # Subscriber: request network
902    s_req_key = request_network(
903        s_dut,
904        s_dut.droid.wifiAwareCreateNetworkSpecifier(s_disc_id, peer_id_on_sub,
905                                                    None))
906
907    # Publisher & Subscriber: wait for network formation
908    p_net_event_nc = wait_for_event_with_keys(
909        p_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
910        (cconsts.NETWORK_CB_KEY_EVENT,
911         cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
912        (cconsts.NETWORK_CB_KEY_ID, p_req_key))
913    s_net_event_nc = wait_for_event_with_keys(
914        s_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
915        (cconsts.NETWORK_CB_KEY_EVENT,
916         cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
917        (cconsts.NETWORK_CB_KEY_ID, s_req_key))
918
919    # validate no leak of information
920    asserts.assert_false(
921        cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in p_net_event_nc["data"],
922        "Network specifier leak!")
923    asserts.assert_false(
924        cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in s_net_event_nc["data"],
925        "Network specifier leak!")
926
927    # note that Pub <-> Sub since IPv6 are of peer's!
928    p_ipv6 = s_net_event_nc["data"][aconsts.NET_CAP_IPV6]
929    s_ipv6 = p_net_event_nc["data"][aconsts.NET_CAP_IPV6]
930
931    p_net_event_lp = wait_for_event_with_keys(
932        p_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
933        (cconsts.NETWORK_CB_KEY_EVENT,
934         cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
935        (cconsts.NETWORK_CB_KEY_ID, p_req_key))
936    s_net_event_lp = wait_for_event_with_keys(
937        s_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
938        (cconsts.NETWORK_CB_KEY_EVENT,
939         cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
940        (cconsts.NETWORK_CB_KEY_ID, s_req_key))
941
942    p_aware_if = p_net_event_lp["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
943    s_aware_if = s_net_event_lp["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
944
945    return p_req_key, s_req_key, p_aware_if, s_aware_if, p_ipv6, s_ipv6
946
947
948def create_oob_ndp_on_sessions(init_dut, resp_dut, init_id, init_mac, resp_id,
949                               resp_mac):
950    """Create an NDP on top of existing Aware sessions (using OOB discovery)
951
952  Args:
953    init_dut: Initiator device
954    resp_dut: Responder device
955    init_id: Initiator attach session id
956    init_mac: Initiator discovery MAC address
957    resp_id: Responder attach session id
958    resp_mac: Responder discovery MAC address
959  Returns:
960    init_req_key: Initiator network request
961    resp_req_key: Responder network request
962    init_aware_if: Initiator Aware data interface
963    resp_aware_if: Responder Aware data interface
964    init_ipv6: Initiator IPv6 address
965    resp_ipv6: Responder IPv6 address
966  """
967    # Responder: request network
968    resp_req_key = request_network(
969        resp_dut,
970        resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
971            resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, None))
972
973    # Initiator: request network
974    init_req_key = request_network(
975        init_dut,
976        init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
977            init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, None))
978
979    # Initiator & Responder: wait for network formation
980    init_net_event_nc = wait_for_event_with_keys(
981        init_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
982        (cconsts.NETWORK_CB_KEY_EVENT,
983         cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
984        (cconsts.NETWORK_CB_KEY_ID, init_req_key))
985    resp_net_event_nc = wait_for_event_with_keys(
986        resp_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
987        (cconsts.NETWORK_CB_KEY_EVENT,
988         cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
989        (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
990
991    # validate no leak of information
992    asserts.assert_false(
993        cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in init_net_event_nc["data"],
994        "Network specifier leak!")
995    asserts.assert_false(
996        cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in resp_net_event_nc["data"],
997        "Network specifier leak!")
998
999    # note that Init <-> Resp since IPv6 are of peer's!
1000    resp_ipv6 = init_net_event_nc["data"][aconsts.NET_CAP_IPV6]
1001    init_ipv6 = resp_net_event_nc["data"][aconsts.NET_CAP_IPV6]
1002
1003    init_net_event_lp = wait_for_event_with_keys(
1004        init_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
1005        (cconsts.NETWORK_CB_KEY_EVENT,
1006         cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
1007        (cconsts.NETWORK_CB_KEY_ID, init_req_key))
1008    resp_net_event_lp = wait_for_event_with_keys(
1009        resp_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
1010        (cconsts.NETWORK_CB_KEY_EVENT,
1011         cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
1012        (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
1013
1014    init_aware_if = init_net_event_lp['data'][
1015        cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
1016    resp_aware_if = resp_net_event_lp['data'][
1017        cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
1018
1019    return (init_req_key, resp_req_key, init_aware_if, resp_aware_if,
1020            init_ipv6, resp_ipv6)
1021
1022
1023def create_oob_ndp(init_dut, resp_dut):
1024    """Create an NDP (using OOB discovery)
1025
1026  Args:
1027    init_dut: Initiator device
1028    resp_dut: Responder device
1029  """
1030    init_dut.pretty_name = 'Initiator'
1031    resp_dut.pretty_name = 'Responder'
1032
1033    # Initiator+Responder: attach and wait for confirmation & identity
1034    init_id = init_dut.droid.wifiAwareAttach(True)
1035    wait_for_event(init_dut, aconsts.EVENT_CB_ON_ATTACHED)
1036    init_ident_event = wait_for_event(init_dut,
1037                                      aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
1038    init_mac = init_ident_event['data']['mac']
1039    resp_id = resp_dut.droid.wifiAwareAttach(True)
1040    wait_for_event(resp_dut, aconsts.EVENT_CB_ON_ATTACHED)
1041    resp_ident_event = wait_for_event(resp_dut,
1042                                      aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
1043    resp_mac = resp_ident_event['data']['mac']
1044
1045    # wait for for devices to synchronize with each other - there are no other
1046    # mechanisms to make sure this happens for OOB discovery (except retrying
1047    # to execute the data-path request)
1048    time.sleep(WAIT_FOR_CLUSTER)
1049
1050    (init_req_key, resp_req_key, init_aware_if, resp_aware_if, init_ipv6,
1051     resp_ipv6) = create_oob_ndp_on_sessions(init_dut, resp_dut, init_id,
1052                                             init_mac, resp_id, resp_mac)
1053
1054    return (init_req_key, resp_req_key, init_aware_if, resp_aware_if,
1055            init_ipv6, resp_ipv6)
1056