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