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