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 __init__(self, controllers): 39 super(ThroughputTest, self).__init__(controllers) 40 41 def request_network(self, dut, ns): 42 """Request a Wi-Fi Aware network. 43 44 Args: 45 dut: Device 46 ns: Network specifier 47 Returns: the request key 48 """ 49 network_req = {"TransportType": 5, "NetworkSpecifier": ns} 50 return dut.droid.connectivityRequestWifiAwareNetwork(network_req) 51 52 def run_iperf_single_ndp_aware_only(self, use_ib, results): 53 """Measure iperf performance on a single NDP, with Aware enabled and no 54 infrastructure connection - i.e. device is not associated to an AP. 55 56 Args: 57 use_ib: True to use in-band discovery, False to use out-of-band discovery. 58 results: Dictionary into which to place test results. 59 """ 60 init_dut = self.android_devices[0] 61 resp_dut = self.android_devices[1] 62 63 if use_ib: 64 # note: Publisher = Responder, Subscribe = Initiator 65 (resp_req_key, init_req_key, resp_aware_if, 66 init_aware_if, resp_ipv6, init_ipv6) = autils.create_ib_ndp( 67 resp_dut, init_dut, 68 autils.create_discovery_config(self.SERVICE_NAME, 69 aconsts.PUBLISH_TYPE_UNSOLICITED), 70 autils.create_discovery_config(self.SERVICE_NAME, 71 aconsts.SUBSCRIBE_TYPE_PASSIVE), 72 self.device_startup_offset) 73 else: 74 (init_req_key, resp_req_key, init_aware_if, resp_aware_if, init_ipv6, 75 resp_ipv6) = autils.create_oob_ndp(init_dut, resp_dut) 76 self.log.info("Interface names: I=%s, R=%s", init_aware_if, resp_aware_if) 77 self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6, 78 resp_ipv6) 79 80 # Run iperf3 81 result, data = init_dut.run_iperf_server("-D") 82 asserts.assert_true(result, "Can't start iperf3 server") 83 84 result, data = resp_dut.run_iperf_client( 85 "%s%%%s" % (init_ipv6, resp_aware_if), "-6 -J") 86 self.log.debug(data) 87 asserts.assert_true(result, 88 "Failure starting/running iperf3 in client mode") 89 self.log.debug(pprint.pformat(data)) 90 91 # clean-up 92 resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key) 93 init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key) 94 95 # Collect results 96 data_json = json.loads("".join(data)) 97 if "error" in data_json: 98 asserts.fail( 99 "iperf run failed: %s" % data_json["error"], extras=data_json) 100 results["tx_rate"] = data_json["end"]["sum_sent"]["bits_per_second"] 101 results["rx_rate"] = data_json["end"]["sum_received"]["bits_per_second"] 102 self.log.info("iPerf3: Sent = %d bps Received = %d bps", results["tx_rate"], 103 results["rx_rate"]) 104 105 def run_iperf(self, q, dut, peer_dut, peer_aware_if, dut_ipv6, port): 106 """Runs iperf and places results in the queue. 107 108 Args: 109 q: The queue into which to place the results 110 dut: The DUT on which to run the iperf server command. 111 peer_dut: The DUT on which to run the iperf client command. 112 peer_aware_if: The interface on the DUT. 113 dut_ipv6: The IPv6 address of the server. 114 port: The port to use for the server and client. 115 """ 116 result, data = dut.run_iperf_server("-D -p %d" % port) 117 asserts.assert_true(result, "Can't start iperf3 server") 118 119 result, data = peer_dut.run_iperf_client( 120 "%s%%%s" % (dut_ipv6, peer_aware_if), "-6 -J -p %d" % port) 121 self.log.debug(data) 122 q.put((result, data)) 123 124 def run_iperf_max_ndp_aware_only(self, results): 125 """Measure iperf performance on the max number of concurrent OOB NDPs, with 126 Aware enabled and no infrastructure connection - i.e. device is not 127 associated to an AP. 128 129 Note: the test requires MAX_NDP + 1 devices to be validated. If these are 130 not available the test will fail. 131 132 Args: 133 results: Dictionary into which to place test results. 134 """ 135 dut = self.android_devices[0] 136 137 # get max NDP: using first available device (assumes all devices are the 138 # same) 139 max_ndp = dut.aware_capabilities[aconsts.CAP_MAX_NDP_SESSIONS] 140 asserts.assert_true(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, init_ipv6, 153 resp_ipv6) = autils.create_oob_ndp(dut, self.android_devices[i + 1]) 154 self.log.info("Interface names: I=%s, R=%s", init_aware_if, resp_aware_if) 155 self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6, 156 resp_ipv6) 157 158 dut_requests.append(init_req_key) 159 peers_requests.append(resp_req_key) 160 if dut_aware_if is None: 161 dut_aware_if = init_aware_if 162 else: 163 asserts.assert_equal( 164 dut_aware_if, init_aware_if, 165 "DUT (Initiator) interface changed on subsequent NDPs!?") 166 if dut_ipv6 is None: 167 dut_ipv6 = init_ipv6 168 else: 169 asserts.assert_equal( 170 dut_ipv6, init_ipv6, 171 "DUT (Initiator) IPv6 changed on subsequent NDPs!?") 172 peers_aware_ifs.append(resp_aware_if) 173 peers_ipv6s.append(resp_ipv6) 174 175 # create threads, start them, and wait for all to finish 176 base_port = 5000 177 q = queue.Queue() 178 threads = [] 179 for i in range(max_ndp): 180 threads.append( 181 threading.Thread( 182 target=self.run_iperf, 183 args=(q, dut, self.android_devices[i + 1], peers_aware_ifs[i], 184 dut_ipv6, base_port + i))) 185 186 for thread in threads: 187 thread.start() 188 189 for thread in threads: 190 thread.join() 191 192 # cleanup 193 for i in range(max_ndp): 194 dut.droid.connectivityUnregisterNetworkCallback(dut_requests[i]) 195 self.android_devices[i + 1].droid.connectivityUnregisterNetworkCallback( 196 peers_requests[i]) 197 198 # collect data 199 for i in range(max_ndp): 200 results[i] = {} 201 result, data = q.get() 202 asserts.assert_true(result, 203 "Failure starting/running iperf3 in client mode") 204 self.log.debug(pprint.pformat(data)) 205 data_json = json.loads("".join(data)) 206 if "error" in data_json: 207 asserts.fail( 208 "iperf run failed: %s" % data_json["error"], extras=data_json) 209 results[i]["tx_rate"] = data_json["end"]["sum_sent"]["bits_per_second"] 210 results[i]["rx_rate"] = data_json["end"]["sum_received"][ 211 "bits_per_second"] 212 self.log.info("iPerf3: Sent = %d bps Received = %d bps", 213 results[i]["tx_rate"], results[i]["rx_rate"]) 214 215 ######################################################################## 216 217 def test_iperf_single_ndp_aware_only_ib(self): 218 """Measure throughput using iperf on a single NDP, with Aware enabled and 219 no infrastructure connection. Use in-band discovery.""" 220 results = {} 221 self.run_iperf_single_ndp_aware_only(use_ib=True, results=results) 222 asserts.explicit_pass( 223 "test_iperf_single_ndp_aware_only_ib passes", extras=results) 224 225 def test_iperf_single_ndp_aware_only_oob(self): 226 """Measure throughput using iperf on a single NDP, with Aware enabled and 227 no infrastructure connection. Use out-of-band discovery.""" 228 results = {} 229 self.run_iperf_single_ndp_aware_only(use_ib=False, results=results) 230 asserts.explicit_pass( 231 "test_iperf_single_ndp_aware_only_oob passes", extras=results) 232 233 def test_iperf_max_ndp_aware_only_oob(self): 234 """Measure throughput using iperf on all possible concurrent NDPs, with 235 Aware enabled and no infrastructure connection. Use out-of-band discovery. 236 """ 237 results = {} 238 self.run_iperf_max_ndp_aware_only(results=results) 239 asserts.explicit_pass( 240 "test_iperf_max_ndp_aware_only_oob passes", extras=results) 241 242 ######################################################################## 243 244 def run_iperf_max_ndi_aware_only(self, sec_configs, results): 245 """Measure iperf performance on multiple NDPs between 2 devices using 246 different security configurations (and hence different NDIs). Test with 247 Aware enabled and no infrastructure connection - i.e. device is not 248 associated to an AP. 249 250 The security configuration can be: 251 - None: open 252 - String: passphrase 253 - otherwise: PMK (byte array) 254 255 Args: 256 sec_configs: list of security configurations 257 results: Dictionary into which to place test results. 258 """ 259 init_dut = self.android_devices[0] 260 init_dut.pretty_name = "Initiator" 261 resp_dut = self.android_devices[1] 262 resp_dut.pretty_name = "Responder" 263 264 asserts.skip_if(init_dut.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES] 265 < len(sec_configs) or 266 resp_dut.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES] 267 < len(sec_configs), 268 "Initiator or Responder do not support multiple NDIs") 269 270 271 init_id, init_mac = autils.attach_with_identity(init_dut) 272 resp_id, resp_mac = autils.attach_with_identity(resp_dut) 273 274 # wait for for devices to synchronize with each other - there are no other 275 # mechanisms to make sure this happens for OOB discovery (except retrying 276 # to execute the data-path request) 277 time.sleep(autils.WAIT_FOR_CLUSTER) 278 279 resp_req_keys = [] 280 init_req_keys = [] 281 resp_aware_ifs = [] 282 init_aware_ifs = [] 283 resp_aware_ipv6s = [] 284 init_aware_ipv6s = [] 285 286 for sec in sec_configs: 287 # Responder: request network 288 resp_req_key = autils.request_network(resp_dut, 289 autils.get_network_specifier( 290 resp_dut, resp_id, 291 aconsts.DATA_PATH_RESPONDER, 292 init_mac, sec)) 293 resp_req_keys.append(resp_req_key) 294 295 # Initiator: request network 296 init_req_key = autils.request_network(init_dut, 297 autils.get_network_specifier( 298 init_dut, init_id, 299 aconsts.DATA_PATH_INITIATOR, 300 resp_mac, sec)) 301 init_req_keys.append(init_req_key) 302 303 # Wait for network 304 init_net_event = autils.wait_for_event_with_keys( 305 init_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT, 306 (cconsts.NETWORK_CB_KEY_EVENT, 307 cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED), 308 (cconsts.NETWORK_CB_KEY_ID, init_req_key)) 309 resp_net_event = autils.wait_for_event_with_keys( 310 resp_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT, 311 (cconsts.NETWORK_CB_KEY_EVENT, 312 cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED), 313 (cconsts.NETWORK_CB_KEY_ID, resp_req_key)) 314 315 resp_aware_ifs.append( 316 resp_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]) 317 init_aware_ifs.append( 318 init_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]) 319 320 resp_aware_ipv6s.append( 321 autils.get_ipv6_addr(resp_dut, resp_aware_ifs[-1])) 322 init_aware_ipv6s.append( 323 autils.get_ipv6_addr(init_dut, init_aware_ifs[-1])) 324 325 self.log.info("Initiator interfaces/ipv6: %s / %s", init_aware_ifs, 326 init_aware_ipv6s) 327 self.log.info("Responder interfaces/ipv6: %s / %s", resp_aware_ifs, 328 resp_aware_ipv6s) 329 330 # create threads, start them, and wait for all to finish 331 base_port = 5000 332 q = queue.Queue() 333 threads = [] 334 for i in range(len(sec_configs)): 335 threads.append( 336 threading.Thread( 337 target=self.run_iperf, 338 args=(q, init_dut, resp_dut, resp_aware_ifs[i], init_aware_ipv6s[ 339 i], base_port + i))) 340 341 for thread in threads: 342 thread.start() 343 344 for thread in threads: 345 thread.join() 346 347 # release requests 348 for resp_req_key in resp_req_keys: 349 resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key) 350 for init_req_key in init_req_keys: 351 init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key) 352 353 354 # collect data 355 for i in range(len(sec_configs)): 356 results[i] = {} 357 result, data = q.get() 358 asserts.assert_true(result, 359 "Failure starting/running iperf3 in client mode") 360 self.log.debug(pprint.pformat(data)) 361 data_json = json.loads("".join(data)) 362 if "error" in data_json: 363 asserts.fail( 364 "iperf run failed: %s" % data_json["error"], extras=data_json) 365 results[i]["tx_rate"] = data_json["end"]["sum_sent"]["bits_per_second"] 366 results[i]["rx_rate"] = data_json["end"]["sum_received"][ 367 "bits_per_second"] 368 self.log.info("iPerf3: Sent = %d bps Received = %d bps", 369 results[i]["tx_rate"], results[i]["rx_rate"]) 370 371 def test_iperf_max_ndi_aware_only_passphrases(self): 372 """Test throughput for multiple NDIs configured with different passphrases. 373 """ 374 results = {} 375 self.run_iperf_max_ndi_aware_only( 376 [self.PASSPHRASE, self.PASSPHRASE2], results=results) 377 asserts.explicit_pass( 378 "test_iperf_max_ndi_aware_only_passphrases passes", extras=results) 379