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 471def run_ping6(dut, target_ip, duration=60): 472 """Run ping test and return the latency result 473 474 Args: 475 dut: the dut which run the ping cmd 476 target_ip: target IP Address for ping 477 duration: the duration time of the ping 478 479 return: dict contains "min/avg/max/mdev" result 480 """ 481 cmd = "ping6 -w %d %s" % (duration, target_ip) 482 ping_result = dut.adb.shell(cmd, timeout=duration + 1) 483 res = re.match(".*mdev = (\S+) .*", ping_result, re.S) 484 asserts.assert_true(res, "Cannot reach the IP address %s", target_ip) 485 title = ["min", "avg", "max", "mdev"] 486 result = res.group(1).split("/") 487 latency_result = {} 488 for i in range(len(title)): 489 latency_result[title[i]] = result[i] 490 return latency_result 491 492 493######################################################### 494# Aware primitives 495######################################################### 496 497 498def request_network(dut, ns): 499 """Request a Wi-Fi Aware network. 500 501 Args: 502 dut: Device 503 ns: Network specifier 504 Returns: the request key 505 """ 506 network_req = {"TransportType": 5, "NetworkSpecifier": ns} 507 return dut.droid.connectivityRequestWifiAwareNetwork(network_req) 508 509 510def get_network_specifier(dut, id, dev_type, peer_mac, sec): 511 """Create a network specifier for the device based on the security 512 configuration. 513 514 Args: 515 dut: device 516 id: session ID 517 dev_type: device type - Initiator or Responder 518 peer_mac: the discovery MAC address of the peer 519 sec: security configuration 520 """ 521 if sec is None: 522 return dut.droid.wifiAwareCreateNetworkSpecifierOob( 523 id, dev_type, peer_mac) 524 if isinstance(sec, str): 525 return dut.droid.wifiAwareCreateNetworkSpecifierOob( 526 id, dev_type, peer_mac, sec) 527 return dut.droid.wifiAwareCreateNetworkSpecifierOob( 528 id, dev_type, peer_mac, None, sec) 529 530 531def configure_power_setting(device, mode, name, value): 532 """Use the command-line API to configure the power setting 533 534 Args: 535 device: Device on which to perform configuration 536 mode: The power mode being set, should be "default", "inactive", or "idle" 537 name: One of the power settings from 'wifiaware set-power'. 538 value: An integer. 539 """ 540 device.adb.shell( 541 "cmd wifiaware native_api set-power %s %s %d" % (mode, name, value)) 542 543 544def configure_mac_random_interval(device, interval_sec): 545 """Use the command-line API to configure the MAC address randomization 546 interval. 547 548 Args: 549 device: Device on which to perform configuration 550 interval_sec: The MAC randomization interval in seconds. A value of 0 551 disables all randomization. 552 """ 553 device.adb.shell("cmd wifiaware native_api set mac_random_interval_sec %d" 554 % interval_sec) 555 556 557def configure_ndp_allow_any_override(device, override_api_check): 558 """Use the command-line API to configure whether an NDP Responder may be 559 configured to accept an NDP request from ANY peer. 560 561 By default the target API level of the requesting app determines whether such 562 configuration is permitted. This allows overriding the API check and allowing 563 it. 564 565 Args: 566 device: Device on which to perform configuration. 567 override_api_check: True to allow a Responder to ANY configuration, False to 568 perform the API level check. 569 """ 570 device.adb.shell("cmd wifiaware state_mgr allow_ndp_any %s" % 571 ("true" if override_api_check else "false")) 572 573 574def config_settings_high_power(device): 575 """Configure device's power settings values to high power mode - 576 whether device is in interactive or non-interactive modes""" 577 configure_power_setting(device, "default", "dw_24ghz", 578 aconsts.POWER_DW_24_INTERACTIVE) 579 configure_power_setting(device, "default", "dw_5ghz", 580 aconsts.POWER_DW_5_INTERACTIVE) 581 configure_power_setting(device, "default", "disc_beacon_interval_ms", 582 aconsts.POWER_DISC_BEACON_INTERVAL_INTERACTIVE) 583 configure_power_setting(device, "default", "num_ss_in_discovery", 584 aconsts.POWER_NUM_SS_IN_DISC_INTERACTIVE) 585 configure_power_setting(device, "default", "enable_dw_early_term", 586 aconsts.POWER_ENABLE_DW_EARLY_TERM_INTERACTIVE) 587 588 configure_power_setting(device, "inactive", "dw_24ghz", 589 aconsts.POWER_DW_24_INTERACTIVE) 590 configure_power_setting(device, "inactive", "dw_5ghz", 591 aconsts.POWER_DW_5_INTERACTIVE) 592 configure_power_setting(device, "inactive", "disc_beacon_interval_ms", 593 aconsts.POWER_DISC_BEACON_INTERVAL_INTERACTIVE) 594 configure_power_setting(device, "inactive", "num_ss_in_discovery", 595 aconsts.POWER_NUM_SS_IN_DISC_INTERACTIVE) 596 configure_power_setting(device, "inactive", "enable_dw_early_term", 597 aconsts.POWER_ENABLE_DW_EARLY_TERM_INTERACTIVE) 598 599 600def config_settings_low_power(device): 601 """Configure device's power settings values to low power mode - whether 602 device is in interactive or non-interactive modes""" 603 configure_power_setting(device, "default", "dw_24ghz", 604 aconsts.POWER_DW_24_NON_INTERACTIVE) 605 configure_power_setting(device, "default", "dw_5ghz", 606 aconsts.POWER_DW_5_NON_INTERACTIVE) 607 configure_power_setting(device, "default", "disc_beacon_interval_ms", 608 aconsts.POWER_DISC_BEACON_INTERVAL_NON_INTERACTIVE) 609 configure_power_setting(device, "default", "num_ss_in_discovery", 610 aconsts.POWER_NUM_SS_IN_DISC_NON_INTERACTIVE) 611 configure_power_setting(device, "default", "enable_dw_early_term", 612 aconsts.POWER_ENABLE_DW_EARLY_TERM_NON_INTERACTIVE) 613 614 configure_power_setting(device, "inactive", "dw_24ghz", 615 aconsts.POWER_DW_24_NON_INTERACTIVE) 616 configure_power_setting(device, "inactive", "dw_5ghz", 617 aconsts.POWER_DW_5_NON_INTERACTIVE) 618 configure_power_setting(device, "inactive", "disc_beacon_interval_ms", 619 aconsts.POWER_DISC_BEACON_INTERVAL_NON_INTERACTIVE) 620 configure_power_setting(device, "inactive", "num_ss_in_discovery", 621 aconsts.POWER_NUM_SS_IN_DISC_NON_INTERACTIVE) 622 configure_power_setting(device, "inactive", "enable_dw_early_term", 623 aconsts.POWER_ENABLE_DW_EARLY_TERM_NON_INTERACTIVE) 624 625 626def config_power_settings(device, 627 dw_24ghz, 628 dw_5ghz, 629 disc_beacon_interval=None, 630 num_ss_in_disc=None, 631 enable_dw_early_term=None): 632 """Configure device's discovery window (DW) values to the specified values - 633 whether the device is in interactive or non-interactive mode. 634 635 Args: 636 dw_24ghz: DW interval in the 2.4GHz band. 637 dw_5ghz: DW interval in the 5GHz band. 638 disc_beacon_interval: The discovery beacon interval (in ms). If None then 639 not set. 640 num_ss_in_disc: Number of spatial streams to use for discovery. If None then 641 not set. 642 enable_dw_early_term: If True then enable early termination of the DW. If 643 None then not set. 644 """ 645 configure_power_setting(device, "default", "dw_24ghz", dw_24ghz) 646 configure_power_setting(device, "default", "dw_5ghz", dw_5ghz) 647 configure_power_setting(device, "inactive", "dw_24ghz", dw_24ghz) 648 configure_power_setting(device, "inactive", "dw_5ghz", dw_5ghz) 649 650 if disc_beacon_interval is not None: 651 configure_power_setting(device, "default", "disc_beacon_interval_ms", 652 disc_beacon_interval) 653 configure_power_setting(device, "inactive", "disc_beacon_interval_ms", 654 disc_beacon_interval) 655 656 if num_ss_in_disc is not None: 657 configure_power_setting(device, "default", "num_ss_in_discovery", 658 num_ss_in_disc) 659 configure_power_setting(device, "inactive", "num_ss_in_discovery", 660 num_ss_in_disc) 661 662 if enable_dw_early_term is not None: 663 configure_power_setting(device, "default", "enable_dw_early_term", 664 enable_dw_early_term) 665 configure_power_setting(device, "inactive", "enable_dw_early_term", 666 enable_dw_early_term) 667 668 669def create_discovery_config(service_name, 670 d_type, 671 ssi=None, 672 match_filter=None, 673 match_filter_list=None, 674 ttl=0, 675 term_cb_enable=True): 676 """Create a publish discovery configuration based on input parameters. 677 678 Args: 679 service_name: Service name - required 680 d_type: Discovery type (publish or subscribe constants) 681 ssi: Supplemental information - defaults to None 682 match_filter, match_filter_list: The match_filter, only one mechanism can 683 be used to specify. Defaults to None. 684 ttl: Time-to-live - defaults to 0 (i.e. non-self terminating) 685 term_cb_enable: True (default) to enable callback on termination, False 686 means that no callback is called when session terminates. 687 Returns: 688 publish discovery configuration object. 689 """ 690 config = {} 691 config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = service_name 692 config[aconsts.DISCOVERY_KEY_DISCOVERY_TYPE] = d_type 693 if ssi is not None: 694 config[aconsts.DISCOVERY_KEY_SSI] = ssi 695 if match_filter is not None: 696 config[aconsts.DISCOVERY_KEY_MATCH_FILTER] = match_filter 697 if match_filter_list is not None: 698 config[aconsts.DISCOVERY_KEY_MATCH_FILTER_LIST] = match_filter_list 699 config[aconsts.DISCOVERY_KEY_TTL] = ttl 700 config[aconsts.DISCOVERY_KEY_TERM_CB_ENABLED] = term_cb_enable 701 return config 702 703 704def add_ranging_to_pub(p_config, enable_ranging): 705 """Add ranging enabled configuration to a publish configuration (only relevant 706 for publish configuration). 707 708 Args: 709 p_config: The Publish discovery configuration. 710 enable_ranging: True to enable ranging, False to disable. 711 Returns: 712 The modified publish configuration. 713 """ 714 p_config[aconsts.DISCOVERY_KEY_RANGING_ENABLED] = enable_ranging 715 return p_config 716 717 718def add_ranging_to_sub(s_config, min_distance_mm, max_distance_mm): 719 """Add ranging distance configuration to a subscribe configuration (only 720 relevant to a subscribe configuration). 721 722 Args: 723 s_config: The Subscribe discovery configuration. 724 min_distance_mm, max_distance_mm: The min and max distance specification. 725 Used if not None. 726 Returns: 727 The modified subscribe configuration. 728 """ 729 if min_distance_mm is not None: 730 s_config[aconsts.DISCOVERY_KEY_MIN_DISTANCE_MM] = min_distance_mm 731 if max_distance_mm is not None: 732 s_config[aconsts.DISCOVERY_KEY_MAX_DISTANCE_MM] = max_distance_mm 733 return s_config 734 735 736def attach_with_identity(dut): 737 """Start an Aware session (attach) and wait for confirmation and identity 738 information (mac address). 739 740 Args: 741 dut: Device under test 742 Returns: 743 id: Aware session ID. 744 mac: Discovery MAC address of this device. 745 """ 746 id = dut.droid.wifiAwareAttach(True) 747 wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED) 748 event = wait_for_event(dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED) 749 mac = event["data"]["mac"] 750 751 return id, mac 752 753 754def create_discovery_pair(p_dut, 755 s_dut, 756 p_config, 757 s_config, 758 device_startup_offset, 759 msg_id=None): 760 """Creates a discovery session (publish and subscribe), and waits for 761 service discovery - at that point the sessions are connected and ready for 762 further messaging of data-path setup. 763 764 Args: 765 p_dut: Device to use as publisher. 766 s_dut: Device to use as subscriber. 767 p_config: Publish configuration. 768 s_config: Subscribe configuration. 769 device_startup_offset: Number of seconds to offset the enabling of NAN on 770 the two devices. 771 msg_id: Controls whether a message is sent from Subscriber to Publisher 772 (so that publisher has the sub's peer ID). If None then not sent, 773 otherwise should be an int for the message id. 774 Returns: variable size list of: 775 p_id: Publisher attach session id 776 s_id: Subscriber attach session id 777 p_disc_id: Publisher discovery session id 778 s_disc_id: Subscriber discovery session id 779 peer_id_on_sub: Peer ID of the Publisher as seen on the Subscriber 780 peer_id_on_pub: Peer ID of the Subscriber as seen on the Publisher. Only 781 included if |msg_id| is not None. 782 """ 783 p_dut.pretty_name = 'Publisher' 784 s_dut.pretty_name = 'Subscriber' 785 786 # Publisher+Subscriber: attach and wait for confirmation 787 p_id = p_dut.droid.wifiAwareAttach() 788 wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED) 789 time.sleep(device_startup_offset) 790 s_id = s_dut.droid.wifiAwareAttach() 791 wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED) 792 793 # Publisher: start publish and wait for confirmation 794 p_disc_id = p_dut.droid.wifiAwarePublish(p_id, p_config) 795 wait_for_event(p_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED) 796 797 # Subscriber: start subscribe and wait for confirmation 798 s_disc_id = s_dut.droid.wifiAwareSubscribe(s_id, s_config) 799 wait_for_event(s_dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED) 800 801 # Subscriber: wait for service discovery 802 discovery_event = wait_for_event(s_dut, 803 aconsts.SESSION_CB_ON_SERVICE_DISCOVERED) 804 peer_id_on_sub = discovery_event['data'][aconsts.SESSION_CB_KEY_PEER_ID] 805 806 # Optionally send a message from Subscriber to Publisher 807 if msg_id is not None: 808 ping_msg = 'PING' 809 810 # Subscriber: send message to peer (Publisher) 811 s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub, msg_id, 812 ping_msg, aconsts.MAX_TX_RETRIES) 813 sub_tx_msg_event = wait_for_event(s_dut, 814 aconsts.SESSION_CB_ON_MESSAGE_SENT) 815 asserts.assert_equal( 816 msg_id, 817 sub_tx_msg_event['data'][aconsts.SESSION_CB_KEY_MESSAGE_ID], 818 'Subscriber -> Publisher message ID corrupted') 819 820 # Publisher: wait for received message 821 pub_rx_msg_event = wait_for_event( 822 p_dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED) 823 peer_id_on_pub = pub_rx_msg_event['data'][ 824 aconsts.SESSION_CB_KEY_PEER_ID] 825 asserts.assert_equal( 826 ping_msg, 827 pub_rx_msg_event['data'][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], 828 'Subscriber -> Publisher message corrupted') 829 return p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub, peer_id_on_pub 830 831 return p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub 832 833 834def create_ib_ndp(p_dut, s_dut, p_config, s_config, device_startup_offset): 835 """Create an NDP (using in-band discovery) 836 837 Args: 838 p_dut: Device to use as publisher. 839 s_dut: Device to use as subscriber. 840 p_config: Publish configuration. 841 s_config: Subscribe configuration. 842 device_startup_offset: Number of seconds to offset the enabling of NAN on 843 the two devices. 844 """ 845 (p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub, 846 peer_id_on_pub) = create_discovery_pair( 847 p_dut, s_dut, p_config, s_config, device_startup_offset, msg_id=9999) 848 849 # Publisher: request network 850 p_req_key = request_network( 851 p_dut, 852 p_dut.droid.wifiAwareCreateNetworkSpecifier(p_disc_id, peer_id_on_pub, 853 None)) 854 855 # Subscriber: request network 856 s_req_key = request_network( 857 s_dut, 858 s_dut.droid.wifiAwareCreateNetworkSpecifier(s_disc_id, peer_id_on_sub, 859 None)) 860 861 # Publisher & Subscriber: wait for network formation 862 p_net_event_nc = wait_for_event_with_keys( 863 p_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT, 864 (cconsts.NETWORK_CB_KEY_EVENT, 865 cconsts.NETWORK_CB_CAPABILITIES_CHANGED), 866 (cconsts.NETWORK_CB_KEY_ID, p_req_key)) 867 s_net_event_nc = wait_for_event_with_keys( 868 s_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT, 869 (cconsts.NETWORK_CB_KEY_EVENT, 870 cconsts.NETWORK_CB_CAPABILITIES_CHANGED), 871 (cconsts.NETWORK_CB_KEY_ID, s_req_key)) 872 873 # validate no leak of information 874 asserts.assert_false( 875 cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in p_net_event_nc["data"], 876 "Network specifier leak!") 877 asserts.assert_false( 878 cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in s_net_event_nc["data"], 879 "Network specifier leak!") 880 881 # note that Pub <-> Sub since IPv6 are of peer's! 882 p_ipv6 = s_net_event_nc["data"][aconsts.NET_CAP_IPV6] 883 s_ipv6 = p_net_event_nc["data"][aconsts.NET_CAP_IPV6] 884 885 p_net_event_lp = wait_for_event_with_keys( 886 p_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT, 887 (cconsts.NETWORK_CB_KEY_EVENT, 888 cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED), 889 (cconsts.NETWORK_CB_KEY_ID, p_req_key)) 890 s_net_event_lp = wait_for_event_with_keys( 891 s_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT, 892 (cconsts.NETWORK_CB_KEY_EVENT, 893 cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED), 894 (cconsts.NETWORK_CB_KEY_ID, s_req_key)) 895 896 p_aware_if = p_net_event_lp["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME] 897 s_aware_if = s_net_event_lp["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME] 898 899 return p_req_key, s_req_key, p_aware_if, s_aware_if, p_ipv6, s_ipv6 900 901 902def create_oob_ndp_on_sessions(init_dut, resp_dut, init_id, init_mac, resp_id, 903 resp_mac): 904 """Create an NDP on top of existing Aware sessions (using OOB discovery) 905 906 Args: 907 init_dut: Initiator device 908 resp_dut: Responder device 909 init_id: Initiator attach session id 910 init_mac: Initiator discovery MAC address 911 resp_id: Responder attach session id 912 resp_mac: Responder discovery MAC address 913 Returns: 914 init_req_key: Initiator network request 915 resp_req_key: Responder network request 916 init_aware_if: Initiator Aware data interface 917 resp_aware_if: Responder Aware data interface 918 init_ipv6: Initiator IPv6 address 919 resp_ipv6: Responder IPv6 address 920 """ 921 # Responder: request network 922 resp_req_key = request_network( 923 resp_dut, 924 resp_dut.droid.wifiAwareCreateNetworkSpecifierOob( 925 resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, None)) 926 927 # Initiator: request network 928 init_req_key = request_network( 929 init_dut, 930 init_dut.droid.wifiAwareCreateNetworkSpecifierOob( 931 init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, None)) 932 933 # Initiator & Responder: wait for network formation 934 init_net_event_nc = wait_for_event_with_keys( 935 init_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT, 936 (cconsts.NETWORK_CB_KEY_EVENT, 937 cconsts.NETWORK_CB_CAPABILITIES_CHANGED), 938 (cconsts.NETWORK_CB_KEY_ID, init_req_key)) 939 resp_net_event_nc = wait_for_event_with_keys( 940 resp_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT, 941 (cconsts.NETWORK_CB_KEY_EVENT, 942 cconsts.NETWORK_CB_CAPABILITIES_CHANGED), 943 (cconsts.NETWORK_CB_KEY_ID, resp_req_key)) 944 945 # validate no leak of information 946 asserts.assert_false( 947 cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in init_net_event_nc["data"], 948 "Network specifier leak!") 949 asserts.assert_false( 950 cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in resp_net_event_nc["data"], 951 "Network specifier leak!") 952 953 # note that Init <-> Resp since IPv6 are of peer's! 954 resp_ipv6 = init_net_event_nc["data"][aconsts.NET_CAP_IPV6] 955 init_ipv6 = resp_net_event_nc["data"][aconsts.NET_CAP_IPV6] 956 957 init_net_event_lp = wait_for_event_with_keys( 958 init_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT, 959 (cconsts.NETWORK_CB_KEY_EVENT, 960 cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED), 961 (cconsts.NETWORK_CB_KEY_ID, init_req_key)) 962 resp_net_event_lp = wait_for_event_with_keys( 963 resp_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT, 964 (cconsts.NETWORK_CB_KEY_EVENT, 965 cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED), 966 (cconsts.NETWORK_CB_KEY_ID, resp_req_key)) 967 968 init_aware_if = init_net_event_lp['data'][ 969 cconsts.NETWORK_CB_KEY_INTERFACE_NAME] 970 resp_aware_if = resp_net_event_lp['data'][ 971 cconsts.NETWORK_CB_KEY_INTERFACE_NAME] 972 973 return (init_req_key, resp_req_key, init_aware_if, resp_aware_if, 974 init_ipv6, resp_ipv6) 975 976 977def create_oob_ndp(init_dut, resp_dut): 978 """Create an NDP (using OOB discovery) 979 980 Args: 981 init_dut: Initiator device 982 resp_dut: Responder device 983 """ 984 init_dut.pretty_name = 'Initiator' 985 resp_dut.pretty_name = 'Responder' 986 987 # Initiator+Responder: attach and wait for confirmation & identity 988 init_id = init_dut.droid.wifiAwareAttach(True) 989 wait_for_event(init_dut, aconsts.EVENT_CB_ON_ATTACHED) 990 init_ident_event = wait_for_event(init_dut, 991 aconsts.EVENT_CB_ON_IDENTITY_CHANGED) 992 init_mac = init_ident_event['data']['mac'] 993 resp_id = resp_dut.droid.wifiAwareAttach(True) 994 wait_for_event(resp_dut, aconsts.EVENT_CB_ON_ATTACHED) 995 resp_ident_event = wait_for_event(resp_dut, 996 aconsts.EVENT_CB_ON_IDENTITY_CHANGED) 997 resp_mac = resp_ident_event['data']['mac'] 998 999 # wait for for devices to synchronize with each other - there are no other 1000 # mechanisms to make sure this happens for OOB discovery (except retrying 1001 # to execute the data-path request) 1002 time.sleep(WAIT_FOR_CLUSTER) 1003 1004 (init_req_key, resp_req_key, init_aware_if, resp_aware_if, 1005 init_ipv6, resp_ipv6) = create_oob_ndp_on_sessions( 1006 init_dut, resp_dut, init_id, init_mac, resp_id, resp_mac) 1007 1008 return (init_req_key, resp_req_key, init_aware_if, resp_aware_if, 1009 init_ipv6, resp_ipv6) 1010