1#!/usr/bin/python3.4 2# 3# Copyright 2017 - The Android Open Source Project 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 json 18import pprint 19import queue 20import threading 21import time 22 23from acts import asserts 24from acts.test_utils.net import connectivity_const as cconsts 25from acts.test_utils.wifi.aware import aware_const as aconsts 26from acts.test_utils.wifi.aware import aware_test_utils as autils 27from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest 28 29 30class ThroughputTest(AwareBaseTest): 31 """Set of tests for Wi-Fi Aware to measure latency of Aware operations.""" 32 33 SERVICE_NAME = "GoogleTestServiceXYZ" 34 35 PASSPHRASE = "This is some random passphrase - very very secure!!" 36 PASSPHRASE2 = "This is some random passphrase - very very secure - but diff!!" 37 38 def request_network(self, dut, ns): 39 """Request a Wi-Fi Aware network. 40 41 Args: 42 dut: Device 43 ns: Network specifier 44 Returns: the request key 45 """ 46 network_req = {"TransportType": 5, "NetworkSpecifier": ns} 47 return dut.droid.connectivityRequestWifiAwareNetwork(network_req) 48 49 def run_iperf_single_ndp_aware_only(self, use_ib, results): 50 """Measure iperf performance on a single NDP, with Aware enabled and no 51 infrastructure connection - i.e. device is not associated to an AP. 52 53 Args: 54 use_ib: True to use in-band discovery, False to use out-of-band discovery. 55 results: Dictionary into which to place test results. 56 """ 57 init_dut = self.android_devices[0] 58 resp_dut = self.android_devices[1] 59 60 if use_ib: 61 # note: Publisher = Responder, Subscribe = Initiator 62 (resp_req_key, init_req_key, resp_aware_if, init_aware_if, 63 resp_ipv6, init_ipv6) = autils.create_ib_ndp( 64 resp_dut, init_dut, 65 autils.create_discovery_config( 66 self.SERVICE_NAME, aconsts.PUBLISH_TYPE_UNSOLICITED), 67 autils.create_discovery_config( 68 self.SERVICE_NAME, aconsts.SUBSCRIBE_TYPE_PASSIVE), 69 self.device_startup_offset) 70 else: 71 (init_req_key, resp_req_key, init_aware_if, resp_aware_if, 72 init_ipv6, resp_ipv6) = autils.create_oob_ndp(init_dut, resp_dut) 73 self.log.info("Interface names: I=%s, R=%s", init_aware_if, 74 resp_aware_if) 75 self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6, 76 resp_ipv6) 77 78 # Run iperf3 79 result, data = init_dut.run_iperf_server("-D") 80 asserts.assert_true(result, "Can't start iperf3 server") 81 82 result, data = resp_dut.run_iperf_client("%s" % init_ipv6, "-6 -J") 83 self.log.debug(data) 84 asserts.assert_true(result, 85 "Failure starting/running iperf3 in client mode") 86 self.log.debug(pprint.pformat(data)) 87 88 # clean-up 89 resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key) 90 init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key) 91 92 # Collect results 93 data_json = json.loads("".join(data)) 94 if "error" in data_json: 95 asserts.fail( 96 "iperf run failed: %s" % data_json["error"], extras=data_json) 97 results["tx_rate"] = data_json["end"]["sum_sent"]["bits_per_second"] 98 results["rx_rate"] = data_json["end"]["sum_received"][ 99 "bits_per_second"] 100 self.log.info("iPerf3: Sent = %d bps Received = %d bps", 101 results["tx_rate"], results["rx_rate"]) 102 103 def run_iperf(self, q, dut, peer_dut, peer_aware_if, dut_ipv6, port): 104 """Runs iperf and places results in the queue. 105 106 Args: 107 q: The queue into which to place the results 108 dut: The DUT on which to run the iperf server command. 109 peer_dut: The DUT on which to run the iperf client command. 110 peer_aware_if: The interface on the DUT. 111 dut_ipv6: The IPv6 address of the server. 112 port: The port to use for the server and client. 113 """ 114 result, data = dut.run_iperf_server("-D -p %d" % port) 115 asserts.assert_true(result, "Can't start iperf3 server") 116 117 result, data = peer_dut.run_iperf_client("%s" % dut_ipv6, 118 "-6 -J -p %d" % port) 119 self.log.debug(data) 120 q.put((result, data)) 121 122 def run_iperf_max_ndp_aware_only(self, results): 123 """Measure iperf performance on the max number of concurrent OOB NDPs, with 124 Aware enabled and no infrastructure connection - i.e. device is not 125 associated to an AP. 126 127 Note: the test requires MAX_NDP + 1 devices to be validated. If these are 128 not available the test will fail. 129 130 Args: 131 results: Dictionary into which to place test results. 132 """ 133 dut = self.android_devices[0] 134 135 # get max NDP: using first available device (assumes all devices are the 136 # same) 137 max_ndp = dut.aware_capabilities[aconsts.CAP_MAX_NDP_SESSIONS] 138 asserts.assert_true( 139 len(self.android_devices) > max_ndp, 140 'Needed %d devices to run the test, have %d' % 141 (max_ndp + 1, len(self.android_devices))) 142 143 # create all NDPs 144 dut_aware_if = None 145 dut_ipv6 = None 146 peers_aware_ifs = [] 147 peers_ipv6s = [] 148 dut_requests = [] 149 peers_requests = [] 150 for i in range(max_ndp): 151 (init_req_key, resp_req_key, init_aware_if, resp_aware_if, 152 init_ipv6, resp_ipv6) = autils.create_oob_ndp( 153 dut, self.android_devices[i + 1]) 154 self.log.info("Interface names: I=%s, R=%s", init_aware_if, 155 resp_aware_if) 156 self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6, 157 resp_ipv6) 158 159 dut_requests.append(init_req_key) 160 peers_requests.append(resp_req_key) 161 if dut_aware_if is None: 162 dut_aware_if = init_aware_if 163 else: 164 asserts.assert_equal( 165 dut_aware_if, init_aware_if, 166 "DUT (Initiator) interface changed on subsequent NDPs!?") 167 if dut_ipv6 is None: 168 dut_ipv6 = init_ipv6 169 else: 170 asserts.assert_equal( 171 dut_ipv6, init_ipv6, 172 "DUT (Initiator) IPv6 changed on subsequent NDPs!?") 173 peers_aware_ifs.append(resp_aware_if) 174 peers_ipv6s.append(resp_ipv6) 175 176 # create threads, start them, and wait for all to finish 177 base_port = 5000 178 q = queue.Queue() 179 threads = [] 180 for i in range(max_ndp): 181 threads.append( 182 threading.Thread( 183 target=self.run_iperf, 184 args=(q, dut, self.android_devices[i + 1], 185 peers_aware_ifs[i], dut_ipv6, base_port + i))) 186 187 for thread in threads: 188 thread.start() 189 190 for thread in threads: 191 thread.join() 192 193 # cleanup 194 for i in range(max_ndp): 195 dut.droid.connectivityUnregisterNetworkCallback(dut_requests[i]) 196 self.android_devices[ 197 i + 1].droid.connectivityUnregisterNetworkCallback( 198 peers_requests[i]) 199 200 # collect data 201 for i in range(max_ndp): 202 results[i] = {} 203 result, data = q.get() 204 asserts.assert_true( 205 result, "Failure starting/running iperf3 in client mode") 206 self.log.debug(pprint.pformat(data)) 207 data_json = json.loads("".join(data)) 208 if "error" in data_json: 209 asserts.fail( 210 "iperf run failed: %s" % data_json["error"], 211 extras=data_json) 212 results[i]["tx_rate"] = data_json["end"]["sum_sent"][ 213 "bits_per_second"] 214 results[i]["rx_rate"] = data_json["end"]["sum_received"][ 215 "bits_per_second"] 216 self.log.info("iPerf3: Sent = %d bps Received = %d bps", 217 results[i]["tx_rate"], results[i]["rx_rate"]) 218 219 ######################################################################## 220 221 def test_iperf_single_ndp_aware_only_ib(self): 222 """Measure throughput using iperf on a single NDP, with Aware enabled and 223 no infrastructure connection. Use in-band discovery.""" 224 results = {} 225 self.run_iperf_single_ndp_aware_only(use_ib=True, results=results) 226 asserts.explicit_pass( 227 "test_iperf_single_ndp_aware_only_ib passes", extras=results) 228 229 def test_iperf_single_ndp_aware_only_oob(self): 230 """Measure throughput using iperf on a single NDP, with Aware enabled and 231 no infrastructure connection. Use out-of-band discovery.""" 232 results = {} 233 self.run_iperf_single_ndp_aware_only(use_ib=False, results=results) 234 asserts.explicit_pass( 235 "test_iperf_single_ndp_aware_only_oob passes", extras=results) 236 237 def test_iperf_max_ndp_aware_only_oob(self): 238 """Measure throughput using iperf on all possible concurrent NDPs, with 239 Aware enabled and no infrastructure connection. Use out-of-band discovery. 240 """ 241 results = {} 242 self.run_iperf_max_ndp_aware_only(results=results) 243 asserts.explicit_pass( 244 "test_iperf_max_ndp_aware_only_oob passes", extras=results) 245 246 ######################################################################## 247 248 def run_iperf_max_ndi_aware_only(self, sec_configs, results): 249 """Measure iperf performance on multiple NDPs between 2 devices using 250 different security configurations (and hence different NDIs). Test with 251 Aware enabled and no infrastructure connection - i.e. device is not 252 associated to an AP. 253 254 The security configuration can be: 255 - None: open 256 - String: passphrase 257 - otherwise: PMK (byte array) 258 259 Args: 260 sec_configs: list of security configurations 261 results: Dictionary into which to place test results. 262 """ 263 init_dut = self.android_devices[0] 264 init_dut.pretty_name = "Initiator" 265 resp_dut = self.android_devices[1] 266 resp_dut.pretty_name = "Responder" 267 268 asserts.skip_if( 269 init_dut.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES] < 270 len(sec_configs) 271 or resp_dut.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES] < 272 len(sec_configs), 273 "Initiator or Responder do not support multiple NDIs") 274 275 init_id, init_mac = autils.attach_with_identity(init_dut) 276 resp_id, resp_mac = autils.attach_with_identity(resp_dut) 277 278 # wait for for devices to synchronize with each other - there are no other 279 # mechanisms to make sure this happens for OOB discovery (except retrying 280 # to execute the data-path request) 281 time.sleep(autils.WAIT_FOR_CLUSTER) 282 283 resp_req_keys = [] 284 init_req_keys = [] 285 resp_aware_ifs = [] 286 init_aware_ifs = [] 287 resp_aware_ipv6s = [] 288 init_aware_ipv6s = [] 289 290 for sec in sec_configs: 291 # Responder: request network 292 resp_req_key = autils.request_network( 293 resp_dut, 294 autils.get_network_specifier(resp_dut, resp_id, 295 aconsts.DATA_PATH_RESPONDER, 296 init_mac, sec)) 297 resp_req_keys.append(resp_req_key) 298 299 # Initiator: request network 300 init_req_key = autils.request_network( 301 init_dut, 302 autils.get_network_specifier(init_dut, init_id, 303 aconsts.DATA_PATH_INITIATOR, 304 resp_mac, sec)) 305 init_req_keys.append(init_req_key) 306 307 # Wait for network 308 init_net_event_nc = autils.wait_for_event_with_keys( 309 init_dut, cconsts.EVENT_NETWORK_CALLBACK, 310 autils.EVENT_NDP_TIMEOUT, 311 (cconsts.NETWORK_CB_KEY_EVENT, 312 cconsts.NETWORK_CB_CAPABILITIES_CHANGED), 313 (cconsts.NETWORK_CB_KEY_ID, init_req_key)) 314 resp_net_event_nc = autils.wait_for_event_with_keys( 315 resp_dut, cconsts.EVENT_NETWORK_CALLBACK, 316 autils.EVENT_NDP_TIMEOUT, 317 (cconsts.NETWORK_CB_KEY_EVENT, 318 cconsts.NETWORK_CB_CAPABILITIES_CHANGED), 319 (cconsts.NETWORK_CB_KEY_ID, resp_req_key)) 320 321 # validate no leak of information 322 asserts.assert_false( 323 cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in init_net_event_nc[ 324 "data"], "Network specifier leak!") 325 asserts.assert_false( 326 cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in resp_net_event_nc[ 327 "data"], "Network specifier leak!") 328 329 # note that Init <-> Resp since IPv6 are of peer's! 330 resp_ipv6 = init_net_event_nc["data"][aconsts.NET_CAP_IPV6] 331 init_ipv6 = resp_net_event_nc["data"][aconsts.NET_CAP_IPV6] 332 333 init_net_event_lp = autils.wait_for_event_with_keys( 334 init_dut, cconsts.EVENT_NETWORK_CALLBACK, 335 autils.EVENT_NDP_TIMEOUT, 336 (cconsts.NETWORK_CB_KEY_EVENT, 337 cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED), 338 (cconsts.NETWORK_CB_KEY_ID, init_req_key)) 339 resp_net_event_lp = autils.wait_for_event_with_keys( 340 resp_dut, cconsts.EVENT_NETWORK_CALLBACK, 341 autils.EVENT_NDP_TIMEOUT, 342 (cconsts.NETWORK_CB_KEY_EVENT, 343 cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED), 344 (cconsts.NETWORK_CB_KEY_ID, resp_req_key)) 345 346 resp_aware_ifs.append(resp_net_event_lp["data"][ 347 cconsts.NETWORK_CB_KEY_INTERFACE_NAME]) 348 init_aware_ifs.append(init_net_event_lp["data"][ 349 cconsts.NETWORK_CB_KEY_INTERFACE_NAME]) 350 351 resp_aware_ipv6s.append(resp_ipv6) 352 init_aware_ipv6s.append(init_ipv6) 353 354 self.log.info("Initiator interfaces/ipv6: %s / %s", init_aware_ifs, 355 init_aware_ipv6s) 356 self.log.info("Responder interfaces/ipv6: %s / %s", resp_aware_ifs, 357 resp_aware_ipv6s) 358 359 # create threads, start them, and wait for all to finish 360 base_port = 5000 361 q = queue.Queue() 362 threads = [] 363 for i in range(len(sec_configs)): 364 threads.append( 365 threading.Thread( 366 target=self.run_iperf, 367 args=(q, init_dut, resp_dut, resp_aware_ifs[i], 368 init_aware_ipv6s[i], base_port + i))) 369 370 for thread in threads: 371 thread.start() 372 373 for thread in threads: 374 thread.join() 375 376 # release requests 377 for resp_req_key in resp_req_keys: 378 resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key) 379 for init_req_key in init_req_keys: 380 init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key) 381 382 # collect data 383 for i in range(len(sec_configs)): 384 results[i] = {} 385 result, data = q.get() 386 asserts.assert_true( 387 result, "Failure starting/running iperf3 in client mode") 388 self.log.debug(pprint.pformat(data)) 389 data_json = json.loads("".join(data)) 390 if "error" in data_json: 391 asserts.fail( 392 "iperf run failed: %s" % data_json["error"], 393 extras=data_json) 394 results[i]["tx_rate"] = data_json["end"]["sum_sent"][ 395 "bits_per_second"] 396 results[i]["rx_rate"] = data_json["end"]["sum_received"][ 397 "bits_per_second"] 398 self.log.info("iPerf3: Sent = %d bps Received = %d bps", 399 results[i]["tx_rate"], results[i]["rx_rate"]) 400 401 def test_iperf_max_ndi_aware_only_passphrases(self): 402 """Test throughput for multiple NDIs configured with different passphrases. 403 """ 404 results = {} 405 self.run_iperf_max_ndi_aware_only( 406 [self.PASSPHRASE, self.PASSPHRASE2], results=results) 407 asserts.explicit_pass( 408 "test_iperf_max_ndi_aware_only_passphrases passes", extras=results) 409 410 def run_test_traffic_latency_single_ndp_ib_aware_only_open(self): 411 """Measure IPv6 traffic latency performance(ping) on NDP between 2 devices. 412 Security config is open. 413 """ 414 p_dut = self.android_devices[0] 415 p_dut.pretty_name = "publisher" 416 s_dut = self.android_devices[1] 417 s_dut.pretty_name = "subscriber" 418 ndp_info = autils.create_ib_ndp(p_dut, 419 s_dut, 420 autils.create_discovery_config( 421 self.SERVICE_NAME, aconsts.PUBLISH_TYPE_UNSOLICITED), 422 autils.create_discovery_config( 423 self.SERVICE_NAME, aconsts.SUBSCRIBE_TYPE_PASSIVE), 424 self.device_startup_offset) 425 p_req_key = ndp_info[0] 426 s_req_key = ndp_info[1] 427 p_aware_if = ndp_info[2] 428 s_aware_if = ndp_info[3] 429 p_ipv6 = ndp_info[4] 430 s_ipv6 = ndp_info[5] 431 self.log.info("Interface names: P=%s, S=%s", p_aware_if, s_aware_if) 432 self.log.info("Interface addresses (IPv6): P=%s, S=%s", p_ipv6, s_ipv6) 433 self.log.info("Start ping %s from %s", s_ipv6, p_ipv6) 434 latency_result = autils.run_ping6(p_dut, s_ipv6) 435 self.log.info("The latency results are %s", latency_result) 436 437 def test_traffic_latency_single_ndp_ib_aware_only_open(self): 438 """Test IPv6 traffic latency performance on NDP with security config is open. 439 """ 440 self.run_test_traffic_latency_single_ndp_ib_aware_only_open() 441