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 queue
18import time
19
20from acts import asserts
21from acts.test_decorators import test_tracker_info
22from acts.test_utils.net import connectivity_const as cconsts
23from acts.test_utils.wifi.aware import aware_const as aconsts
24from acts.test_utils.wifi.aware import aware_test_utils as autils
25from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
26
27
28class DataPathStressTest(AwareBaseTest):
29
30    # Number of iterations on create/destroy Attach sessions.
31    ATTACH_ITERATIONS = 2
32
33    # Number of iterations on create/destroy NDP in each discovery session.
34    NDP_ITERATIONS = 50
35
36    # Maximum percentage of NDP setup failures over all iterations
37    MAX_FAILURE_PERCENTAGE = 1
38
39    ################################################################
40
41    def run_oob_ndp_stress(self,
42                           attach_iterations,
43                           ndp_iterations,
44                           trigger_failure_on_index=None):
45        """Run NDP (NAN data-path) stress test creating and destroying Aware
46    attach sessions, discovery sessions, and NDPs.
47
48    Args:
49      attach_iterations: Number of attach sessions.
50      ndp_iterations: Number of NDP to be attempted per attach session.
51      trigger_failure_on_index: Trigger a failure on this NDP iteration (the
52                                mechanism is to request NDP on Initiator
53                                before issuing the requeest on the Responder).
54                                If None then no artificial failure triggered.
55    """
56        init_dut = self.android_devices[0]
57        init_dut.pretty_name = 'Initiator'
58        resp_dut = self.android_devices[1]
59        resp_dut.pretty_name = 'Responder'
60
61        ndp_init_setup_success = 0
62        ndp_init_setup_failures = 0
63        ndp_resp_setup_success = 0
64        ndp_resp_setup_failures = 0
65        ndp_full_socket_success = 0
66
67        for attach_iter in range(attach_iterations):
68            init_id = init_dut.droid.wifiAwareAttach(True)
69            autils.wait_for_event(init_dut, aconsts.EVENT_CB_ON_ATTACHED)
70            init_ident_event = autils.wait_for_event(
71                init_dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
72            init_mac = init_ident_event['data']['mac']
73            time.sleep(self.device_startup_offset)
74            resp_id = resp_dut.droid.wifiAwareAttach(True)
75            autils.wait_for_event(resp_dut, aconsts.EVENT_CB_ON_ATTACHED)
76            resp_ident_event = autils.wait_for_event(
77                resp_dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
78            resp_mac = resp_ident_event['data']['mac']
79
80            # wait for for devices to synchronize with each other - there are no other
81            # mechanisms to make sure this happens for OOB discovery (except retrying
82            # to execute the data-path request)
83            time.sleep(autils.WAIT_FOR_CLUSTER)
84
85            for ndp_iteration in range(ndp_iterations):
86                if trigger_failure_on_index != ndp_iteration:
87                    # Responder: request network
88                    resp_req_key = autils.request_network(
89                        resp_dut,
90                        resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
91                            resp_id, aconsts.DATA_PATH_RESPONDER, init_mac,
92                            None))
93
94                    # Wait a minimal amount of time to let the Responder configure itself
95                    # and be ready for the request. While calling it first may be
96                    # sufficient there are no guarantees that a glitch may slow the
97                    # Responder slightly enough to invert the setup order.
98                    time.sleep(1)
99
100                    # Initiator: request network
101                    init_req_key = autils.request_network(
102                        init_dut,
103                        init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
104                            init_id, aconsts.DATA_PATH_INITIATOR, resp_mac,
105                            None))
106                else:
107                    # Initiator: request network
108                    init_req_key = autils.request_network(
109                        init_dut,
110                        init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
111                            init_id, aconsts.DATA_PATH_INITIATOR, resp_mac,
112                            None))
113
114                    # Wait a minimal amount of time to let the Initiator configure itself
115                    # to guarantee failure!
116                    time.sleep(2)
117
118                    # Responder: request network
119                    resp_req_key = autils.request_network(
120                        resp_dut,
121                        resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
122                            resp_id, aconsts.DATA_PATH_RESPONDER, init_mac,
123                            None))
124
125                init_ipv6 = None
126                resp_ipv6 = None
127
128                # Initiator: wait for network formation
129                got_on_available = False
130                got_on_link_props = False
131                got_on_net_cap = False
132                while not got_on_available or not got_on_link_props or not got_on_net_cap:
133                    try:
134                        nc_event = init_dut.ed.pop_event(
135                            cconsts.EVENT_NETWORK_CALLBACK,
136                            autils.EVENT_NDP_TIMEOUT)
137                        if nc_event['data'][
138                                cconsts.
139                                NETWORK_CB_KEY_EVENT] == cconsts.NETWORK_CB_AVAILABLE:
140                            got_on_available = True
141                        elif (nc_event['data'][cconsts.NETWORK_CB_KEY_EVENT] ==
142                              cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED):
143                            got_on_link_props = True
144                        elif (nc_event['data'][cconsts.NETWORK_CB_KEY_EVENT] ==
145                              cconsts.NETWORK_CB_CAPABILITIES_CHANGED):
146                            got_on_net_cap = True
147                            if aconsts.NET_CAP_IPV6 in nc_event["data"]:
148                                resp_ipv6 = nc_event["data"][
149                                    aconsts.NET_CAP_IPV6]
150                    except queue.Empty:
151                        ndp_init_setup_failures = ndp_init_setup_failures + 1
152                        init_dut.log.info(
153                            '[Initiator] Timed out while waiting for '
154                            'EVENT_NETWORK_CALLBACK')
155                        break
156
157                if got_on_available and got_on_link_props and got_on_net_cap:
158                    ndp_init_setup_success = ndp_init_setup_success + 1
159
160                # Responder: wait for network formation
161                got_on_available = False
162                got_on_link_props = False
163                got_on_net_cap = False
164                while not got_on_available or not got_on_link_props or not got_on_net_cap:
165                    try:
166                        nc_event = resp_dut.ed.pop_event(
167                            cconsts.EVENT_NETWORK_CALLBACK,
168                            autils.EVENT_NDP_TIMEOUT)
169                        if nc_event['data'][
170                                cconsts.
171                                NETWORK_CB_KEY_EVENT] == cconsts.NETWORK_CB_AVAILABLE:
172                            got_on_available = True
173                        elif (nc_event['data'][cconsts.NETWORK_CB_KEY_EVENT] ==
174                              cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED):
175                            got_on_link_props = True
176                        elif (nc_event['data'][cconsts.NETWORK_CB_KEY_EVENT] ==
177                              cconsts.NETWORK_CB_CAPABILITIES_CHANGED):
178                            got_on_net_cap = True
179                            if aconsts.NET_CAP_IPV6 in nc_event["data"]:
180                                init_ipv6 = nc_event["data"][
181                                    aconsts.NET_CAP_IPV6]
182                    except queue.Empty:
183                        ndp_resp_setup_failures = ndp_resp_setup_failures + 1
184                        init_dut.log.info(
185                            '[Responder] Timed out while waiting for '
186                            'EVENT_NETWORK_CALLBACK')
187                        break
188
189                if got_on_available and got_on_link_props and got_on_net_cap:
190                    ndp_resp_setup_success = ndp_resp_setup_success + 1
191
192                # open sockets to test connection
193                if autils.verify_socket_connect(init_dut, resp_dut, init_ipv6,
194                                                resp_ipv6, 0):
195                    if autils.verify_socket_connect(resp_dut, init_dut,
196                                                    resp_ipv6, init_ipv6, 0):
197                        ndp_full_socket_success = ndp_full_socket_success + 1
198
199                # clean-up
200                init_dut.droid.connectivityUnregisterNetworkCallback(
201                    init_req_key)
202                resp_dut.droid.connectivityUnregisterNetworkCallback(
203                    resp_req_key)
204
205            # clean-up at end of iteration
206            init_dut.droid.wifiAwareDestroy(init_id)
207            resp_dut.droid.wifiAwareDestroy(resp_id)
208
209        results = {}
210        results['ndp_init_setup_success'] = ndp_init_setup_success
211        results['ndp_init_setup_failures'] = ndp_init_setup_failures
212        results['ndp_resp_setup_success'] = ndp_resp_setup_success
213        results['ndp_resp_setup_failures'] = ndp_resp_setup_failures
214        results['ndp_full_socket_success'] = ndp_full_socket_success
215        max_failures = (self.MAX_FAILURE_PERCENTAGE * attach_iterations *
216                        ndp_iterations / 100)
217        if max_failures == 0:
218            max_failures = 1
219        if trigger_failure_on_index is not None:
220            max_failures = max_failures + 1  # for the triggered failure
221        asserts.assert_true(
222            (ndp_init_setup_failures + ndp_resp_setup_failures) <
223            (2 * max_failures),
224            'NDP setup failure rate exceeds threshold',
225            extras=results)
226        asserts.explicit_pass("test_oob_ndp_stress* done", extras=results)
227
228    @test_tracker_info(uuid="a20a96ba-e71f-4d31-b850-b88a75381981")
229    def test_oob_ndp_stress(self):
230        """Run NDP (NAN data-path) stress test creating and destroying Aware
231    attach sessions, discovery sessions, and NDPs."""
232        self.run_oob_ndp_stress(self.ATTACH_ITERATIONS, self.NDP_ITERATIONS)
233
234    @test_tracker_info(uuid="1fb4a383-bf1a-411a-a904-489dd9e29c6a")
235    def test_oob_ndp_stress_failure_case(self):
236        """Run NDP (NAN data-path) stress test creating and destroying Aware
237    attach sessions, discovery sessions, and NDPs.
238
239    Verify recovery from failure by triggering an artifical failure and
240    verifying that all subsequent iterations succeed.
241    """
242        self.run_oob_ndp_stress(
243            attach_iterations=1, ndp_iterations=10, trigger_failure_on_index=3)
244