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