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