• 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                            instant_mode=None):
719    """Create a publish discovery configuration based on input parameters.
720
721  Args:
722    service_name: Service name - required
723    d_type: Discovery type (publish or subscribe constants)
724    ssi: Supplemental information - defaults to None
725    match_filter, match_filter_list: The match_filter, only one mechanism can
726                                     be used to specify. Defaults to None.
727    ttl: Time-to-live - defaults to 0 (i.e. non-self terminating)
728    term_cb_enable: True (default) to enable callback on termination, False
729                    means that no callback is called when session terminates.
730    instant_mode: set the band to use instant communication mode, 2G or 5G
731  Returns:
732    publish discovery configuration object.
733  """
734    config = {}
735    config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = service_name
736    config[aconsts.DISCOVERY_KEY_DISCOVERY_TYPE] = d_type
737    if ssi is not None:
738        config[aconsts.DISCOVERY_KEY_SSI] = ssi
739    if match_filter is not None:
740        config[aconsts.DISCOVERY_KEY_MATCH_FILTER] = match_filter
741    if match_filter_list is not None:
742        config[aconsts.DISCOVERY_KEY_MATCH_FILTER_LIST] = match_filter_list
743    if instant_mode is not None:
744        config[aconsts.DISCOVERY_KEY_INSTANT_COMMUNICATION_MODE] = instant_mode
745    config[aconsts.DISCOVERY_KEY_TTL] = ttl
746    config[aconsts.DISCOVERY_KEY_TERM_CB_ENABLED] = term_cb_enable
747    return config
748
749
750def add_ranging_to_pub(p_config, enable_ranging):
751    """Add ranging enabled configuration to a publish configuration (only relevant
752  for publish configuration).
753
754  Args:
755    p_config: The Publish discovery configuration.
756    enable_ranging: True to enable ranging, False to disable.
757  Returns:
758    The modified publish configuration.
759  """
760    p_config[aconsts.DISCOVERY_KEY_RANGING_ENABLED] = enable_ranging
761    return p_config
762
763
764def add_ranging_to_sub(s_config, min_distance_mm, max_distance_mm):
765    """Add ranging distance configuration to a subscribe configuration (only
766  relevant to a subscribe configuration).
767
768  Args:
769    s_config: The Subscribe discovery configuration.
770    min_distance_mm, max_distance_mm: The min and max distance specification.
771                                      Used if not None.
772  Returns:
773    The modified subscribe configuration.
774  """
775    if min_distance_mm is not None:
776        s_config[aconsts.DISCOVERY_KEY_MIN_DISTANCE_MM] = min_distance_mm
777    if max_distance_mm is not None:
778        s_config[aconsts.DISCOVERY_KEY_MAX_DISTANCE_MM] = max_distance_mm
779    return s_config
780
781
782def attach_with_identity(dut):
783    """Start an Aware session (attach) and wait for confirmation and identity
784  information (mac address).
785
786  Args:
787    dut: Device under test
788  Returns:
789    id: Aware session ID.
790    mac: Discovery MAC address of this device.
791  """
792    id = dut.droid.wifiAwareAttach(True)
793    wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
794    event = wait_for_event(dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
795    mac = event["data"]["mac"]
796
797    return id, mac
798
799
800def create_discovery_pair(p_dut,
801                          s_dut,
802                          p_config,
803                          s_config,
804                          device_startup_offset,
805                          msg_id=None):
806    """Creates a discovery session (publish and subscribe), and waits for
807  service discovery - at that point the sessions are connected and ready for
808  further messaging of data-path setup.
809
810  Args:
811    p_dut: Device to use as publisher.
812    s_dut: Device to use as subscriber.
813    p_config: Publish configuration.
814    s_config: Subscribe configuration.
815    device_startup_offset: Number of seconds to offset the enabling of NAN on
816                           the two devices.
817    msg_id: Controls whether a message is sent from Subscriber to Publisher
818            (so that publisher has the sub's peer ID). If None then not sent,
819            otherwise should be an int for the message id.
820  Returns: variable size list of:
821    p_id: Publisher attach session id
822    s_id: Subscriber attach session id
823    p_disc_id: Publisher discovery session id
824    s_disc_id: Subscriber discovery session id
825    peer_id_on_sub: Peer ID of the Publisher as seen on the Subscriber
826    peer_id_on_pub: Peer ID of the Subscriber as seen on the Publisher. Only
827                    included if |msg_id| is not None.
828  """
829    p_dut.pretty_name = 'Publisher'
830    s_dut.pretty_name = 'Subscriber'
831
832    # Publisher+Subscriber: attach and wait for confirmation
833    p_id = p_dut.droid.wifiAwareAttach()
834    wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
835    time.sleep(device_startup_offset)
836    s_id = s_dut.droid.wifiAwareAttach()
837    wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
838
839    # Publisher: start publish and wait for confirmation
840    p_disc_id = p_dut.droid.wifiAwarePublish(p_id, p_config)
841    wait_for_event(p_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
842
843    # Subscriber: start subscribe and wait for confirmation
844    s_disc_id = s_dut.droid.wifiAwareSubscribe(s_id, s_config)
845    wait_for_event(s_dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
846
847    # Subscriber: wait for service discovery
848    discovery_event = wait_for_event(s_dut,
849                                     aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
850    peer_id_on_sub = discovery_event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
851
852    # Optionally send a message from Subscriber to Publisher
853    if msg_id is not None:
854        ping_msg = 'PING'
855
856        # Subscriber: send message to peer (Publisher)
857        s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub, msg_id,
858                                         ping_msg, aconsts.MAX_TX_RETRIES)
859        sub_tx_msg_event = wait_for_event(s_dut,
860                                          aconsts.SESSION_CB_ON_MESSAGE_SENT)
861        asserts.assert_equal(
862            msg_id,
863            sub_tx_msg_event['data'][aconsts.SESSION_CB_KEY_MESSAGE_ID],
864            'Subscriber -> Publisher message ID corrupted')
865
866        # Publisher: wait for received message
867        pub_rx_msg_event = wait_for_event(
868            p_dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED)
869        peer_id_on_pub = pub_rx_msg_event['data'][
870            aconsts.SESSION_CB_KEY_PEER_ID]
871        asserts.assert_equal(
872            ping_msg,
873            pub_rx_msg_event['data'][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING],
874            'Subscriber -> Publisher message corrupted')
875        return p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub, peer_id_on_pub
876
877    return p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub
878
879
880def create_ib_ndp(p_dut, s_dut, p_config, s_config, device_startup_offset):
881    """Create an NDP (using in-band discovery)
882
883  Args:
884    p_dut: Device to use as publisher.
885    s_dut: Device to use as subscriber.
886    p_config: Publish configuration.
887    s_config: Subscribe configuration.
888    device_startup_offset: Number of seconds to offset the enabling of NAN on
889                           the two devices.
890  """
891    (p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub,
892     peer_id_on_pub) = create_discovery_pair(p_dut,
893                                             s_dut,
894                                             p_config,
895                                             s_config,
896                                             device_startup_offset,
897                                             msg_id=9999)
898
899    # Publisher: request network
900    p_req_key = request_network(
901        p_dut,
902        p_dut.droid.wifiAwareCreateNetworkSpecifier(p_disc_id, peer_id_on_pub,
903                                                    None))
904
905    # Subscriber: request network
906    s_req_key = request_network(
907        s_dut,
908        s_dut.droid.wifiAwareCreateNetworkSpecifier(s_disc_id, peer_id_on_sub,
909                                                    None))
910
911    # Publisher & Subscriber: wait for network formation
912    p_net_event_nc = wait_for_event_with_keys(
913        p_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
914        (cconsts.NETWORK_CB_KEY_EVENT,
915         cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
916        (cconsts.NETWORK_CB_KEY_ID, p_req_key))
917    s_net_event_nc = wait_for_event_with_keys(
918        s_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
919        (cconsts.NETWORK_CB_KEY_EVENT,
920         cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
921        (cconsts.NETWORK_CB_KEY_ID, s_req_key))
922
923    # validate no leak of information
924    asserts.assert_false(
925        cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in p_net_event_nc["data"],
926        "Network specifier leak!")
927    asserts.assert_false(
928        cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in s_net_event_nc["data"],
929        "Network specifier leak!")
930
931    # note that Pub <-> Sub since IPv6 are of peer's!
932    p_ipv6 = s_net_event_nc["data"][aconsts.NET_CAP_IPV6]
933    s_ipv6 = p_net_event_nc["data"][aconsts.NET_CAP_IPV6]
934
935    p_net_event_lp = wait_for_event_with_keys(
936        p_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
937        (cconsts.NETWORK_CB_KEY_EVENT,
938         cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
939        (cconsts.NETWORK_CB_KEY_ID, p_req_key))
940    s_net_event_lp = wait_for_event_with_keys(
941        s_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
942        (cconsts.NETWORK_CB_KEY_EVENT,
943         cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
944        (cconsts.NETWORK_CB_KEY_ID, s_req_key))
945
946    p_aware_if = p_net_event_lp["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
947    s_aware_if = s_net_event_lp["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
948
949    return p_req_key, s_req_key, p_aware_if, s_aware_if, p_ipv6, s_ipv6
950
951
952def create_oob_ndp_on_sessions(init_dut, resp_dut, init_id, init_mac, resp_id,
953                               resp_mac):
954    """Create an NDP on top of existing Aware sessions (using OOB discovery)
955
956  Args:
957    init_dut: Initiator device
958    resp_dut: Responder device
959    init_id: Initiator attach session id
960    init_mac: Initiator discovery MAC address
961    resp_id: Responder attach session id
962    resp_mac: Responder discovery MAC address
963  Returns:
964    init_req_key: Initiator network request
965    resp_req_key: Responder network request
966    init_aware_if: Initiator Aware data interface
967    resp_aware_if: Responder Aware data interface
968    init_ipv6: Initiator IPv6 address
969    resp_ipv6: Responder IPv6 address
970  """
971    # Responder: request network
972    resp_req_key = request_network(
973        resp_dut,
974        resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
975            resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, None))
976
977    # Initiator: request network
978    init_req_key = request_network(
979        init_dut,
980        init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
981            init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, None))
982
983    # Initiator & Responder: wait for network formation
984    init_net_event_nc = wait_for_event_with_keys(
985        init_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
986        (cconsts.NETWORK_CB_KEY_EVENT,
987         cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
988        (cconsts.NETWORK_CB_KEY_ID, init_req_key))
989    resp_net_event_nc = wait_for_event_with_keys(
990        resp_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
991        (cconsts.NETWORK_CB_KEY_EVENT,
992         cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
993        (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
994
995    # validate no leak of information
996    asserts.assert_false(
997        cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in init_net_event_nc["data"],
998        "Network specifier leak!")
999    asserts.assert_false(
1000        cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in resp_net_event_nc["data"],
1001        "Network specifier leak!")
1002
1003    # note that Init <-> Resp since IPv6 are of peer's!
1004    resp_ipv6 = init_net_event_nc["data"][aconsts.NET_CAP_IPV6]
1005    init_ipv6 = resp_net_event_nc["data"][aconsts.NET_CAP_IPV6]
1006
1007    init_net_event_lp = wait_for_event_with_keys(
1008        init_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
1009        (cconsts.NETWORK_CB_KEY_EVENT,
1010         cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
1011        (cconsts.NETWORK_CB_KEY_ID, init_req_key))
1012    resp_net_event_lp = wait_for_event_with_keys(
1013        resp_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
1014        (cconsts.NETWORK_CB_KEY_EVENT,
1015         cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
1016        (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
1017
1018    init_aware_if = init_net_event_lp['data'][
1019        cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
1020    resp_aware_if = resp_net_event_lp['data'][
1021        cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
1022
1023    return (init_req_key, resp_req_key, init_aware_if, resp_aware_if,
1024            init_ipv6, resp_ipv6)
1025
1026
1027def create_oob_ndp(init_dut, resp_dut):
1028    """Create an NDP (using OOB discovery)
1029
1030  Args:
1031    init_dut: Initiator device
1032    resp_dut: Responder device
1033  """
1034    init_dut.pretty_name = 'Initiator'
1035    resp_dut.pretty_name = 'Responder'
1036
1037    # Initiator+Responder: attach and wait for confirmation & identity
1038    init_id = init_dut.droid.wifiAwareAttach(True)
1039    wait_for_event(init_dut, aconsts.EVENT_CB_ON_ATTACHED)
1040    init_ident_event = wait_for_event(init_dut,
1041                                      aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
1042    init_mac = init_ident_event['data']['mac']
1043    resp_id = resp_dut.droid.wifiAwareAttach(True)
1044    wait_for_event(resp_dut, aconsts.EVENT_CB_ON_ATTACHED)
1045    resp_ident_event = wait_for_event(resp_dut,
1046                                      aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
1047    resp_mac = resp_ident_event['data']['mac']
1048
1049    # wait for for devices to synchronize with each other - there are no other
1050    # mechanisms to make sure this happens for OOB discovery (except retrying
1051    # to execute the data-path request)
1052    time.sleep(WAIT_FOR_CLUSTER)
1053
1054    (init_req_key, resp_req_key, init_aware_if, resp_aware_if, init_ipv6,
1055     resp_ipv6) = create_oob_ndp_on_sessions(init_dut, resp_dut, init_id,
1056                                             init_mac, resp_id, resp_mac)
1057
1058    return (init_req_key, resp_req_key, init_aware_if, resp_aware_if,
1059            init_ipv6, resp_ipv6)
1060