• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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