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