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
17from acts import asserts
18
19from acts.test_decorators import test_tracker_info
20from acts.test_utils.net import connectivity_const as cconsts
21from acts.test_utils.wifi.aware import aware_const as aconsts
22from acts.test_utils.wifi.aware import aware_test_utils as autils
23from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
24
25
26class CapabilitiesTest(AwareBaseTest):
27  """Set of tests for Wi-Fi Aware Capabilities - verifying that the provided
28  capabilities are real (i.e. available)."""
29
30  SERVICE_NAME = "GoogleTestXYZ"
31
32  def __init__(self, controllers):
33    AwareBaseTest.__init__(self, controllers)
34
35  def create_config(self, dtype, service_name):
36    """Create a discovery configuration based on input parameters.
37
38    Args:
39      dtype: Publish or Subscribe discovery type
40      service_name: Service name.
41
42    Returns:
43      Discovery configuration object.
44    """
45    config = {}
46    config[aconsts.DISCOVERY_KEY_DISCOVERY_TYPE] = dtype
47    config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = service_name
48    return config
49
50  def start_discovery_session(self, dut, session_id, is_publish, dtype,
51                              service_name, expect_success):
52    """Start a discovery session
53
54    Args:
55      dut: Device under test
56      session_id: ID of the Aware session in which to start discovery
57      is_publish: True for a publish session, False for subscribe session
58      dtype: Type of the discovery session
59      service_name: Service name to use for the discovery session
60      expect_success: True if expect session to be created, False otherwise
61
62    Returns:
63      Discovery session ID.
64    """
65    config = {}
66    config[aconsts.DISCOVERY_KEY_DISCOVERY_TYPE] = dtype
67    config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = service_name
68
69    if is_publish:
70      disc_id = dut.droid.wifiAwarePublish(session_id, config)
71      event_name = aconsts.SESSION_CB_ON_PUBLISH_STARTED
72    else:
73      disc_id = dut.droid.wifiAwareSubscribe(session_id, config)
74      event_name = aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED
75
76    if expect_success:
77      autils.wait_for_event(dut, event_name)
78    else:
79      autils.wait_for_event(dut, aconsts.SESSION_CB_ON_SESSION_CONFIG_FAILED)
80
81    return disc_id
82
83  ###############################
84
85  @test_tracker_info(uuid="45da8a41-6c02-4434-9eb9-aa0a36ff9f65")
86  def test_max_discovery_sessions(self):
87    """Validate that the device can create as many discovery sessions as are
88    indicated in the device capabilities
89    """
90    dut = self.android_devices[0]
91
92    # attach
93    session_id = dut.droid.wifiAwareAttach(True)
94    autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
95
96    service_name_template = 'GoogleTestService-%s-%d'
97
98    # start the max number of publish sessions
99    for i in range(dut.aware_capabilities[aconsts.CAP_MAX_PUBLISHES]):
100      # create publish discovery session of both types
101      pub_disc_id = self.start_discovery_session(
102          dut, session_id, True, aconsts.PUBLISH_TYPE_UNSOLICITED
103          if i % 2 == 0 else aconsts.PUBLISH_TYPE_SOLICITED,
104          service_name_template % ('pub', i), True)
105
106    # start the max number of subscribe sessions
107    for i in range(dut.aware_capabilities[aconsts.CAP_MAX_SUBSCRIBES]):
108      # create publish discovery session of both types
109      sub_disc_id = self.start_discovery_session(
110          dut, session_id, False, aconsts.SUBSCRIBE_TYPE_PASSIVE
111          if i % 2 == 0 else aconsts.SUBSCRIBE_TYPE_ACTIVE,
112          service_name_template % ('sub', i), True)
113
114    # start another publish & subscribe and expect failure
115    self.start_discovery_session(dut, session_id, True,
116                                 aconsts.PUBLISH_TYPE_UNSOLICITED,
117                                 service_name_template % ('pub', 900), False)
118    self.start_discovery_session(dut, session_id, False,
119                                 aconsts.SUBSCRIBE_TYPE_ACTIVE,
120                                 service_name_template % ('pub', 901), False)
121
122    # delete one of the publishes and try again (see if can create subscribe
123    # instead - should not)
124    dut.droid.wifiAwareDestroyDiscoverySession(pub_disc_id)
125    self.start_discovery_session(dut, session_id, False,
126                                 aconsts.SUBSCRIBE_TYPE_ACTIVE,
127                                 service_name_template % ('pub', 902), False)
128    self.start_discovery_session(dut, session_id, True,
129                                 aconsts.PUBLISH_TYPE_UNSOLICITED,
130                                 service_name_template % ('pub', 903), True)
131
132    # delete one of the subscribes and try again (see if can create publish
133    # instead - should not)
134    dut.droid.wifiAwareDestroyDiscoverySession(sub_disc_id)
135    self.start_discovery_session(dut, session_id, True,
136                                 aconsts.PUBLISH_TYPE_UNSOLICITED,
137                                 service_name_template % ('pub', 904), False)
138    self.start_discovery_session(dut, session_id, False,
139                                 aconsts.SUBSCRIBE_TYPE_ACTIVE,
140                                 service_name_template % ('pub', 905), True)
141
142  def test_max_ndp(self):
143    """Validate that the device can create as many NDPs as are specified
144    by its capabilities.
145
146    Mechanics:
147    - Publisher on DUT (first device)
148    - Subscribers on all other devices
149    - On discovery set up NDP
150
151    Note: the test requires MAX_NDP + 2 devices to be validated. If these are
152    not available the test will fail.
153    """
154    dut = self.android_devices[0]
155
156    # get max NDP: using first available device (assumes all devices are the
157    # same)
158    max_ndp = dut.aware_capabilities[aconsts.CAP_MAX_NDP_SESSIONS]
159
160    # get number of attached devices: needs to be max_ndp+2 to allow for max_ndp
161    # NDPs + an additional one expected to fail.
162    # However, will run the test with max_ndp+1 devices to verify that at least
163    # that many NDPs can be created. Will still fail at the end to indicate that
164    # full test was not run.
165    num_peer_devices = min(len(self.android_devices) - 1, max_ndp + 1)
166    asserts.assert_true(
167        num_peer_devices >= max_ndp,
168        'A minimum of %d devices is needed to run the test, have %d' %
169        (max_ndp + 1, len(self.android_devices)))
170
171    # attach
172    session_id = dut.droid.wifiAwareAttach()
173    autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
174
175    # start publisher
176    p_disc_id = self.start_discovery_session(
177        dut,
178        session_id,
179        is_publish=True,
180        dtype=aconsts.PUBLISH_TYPE_UNSOLICITED,
181        service_name=self.SERVICE_NAME,
182        expect_success=True)
183
184    # loop over other DUTs
185    for i in range(num_peer_devices):
186      other_dut = self.android_devices[i + 1]
187
188      # attach
189      other_session_id = other_dut.droid.wifiAwareAttach()
190      autils.wait_for_event(other_dut, aconsts.EVENT_CB_ON_ATTACHED)
191
192      # start subscriber
193      s_disc_id = self.start_discovery_session(
194          other_dut,
195          other_session_id,
196          is_publish=False,
197          dtype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
198          service_name=self.SERVICE_NAME,
199          expect_success=True)
200
201      discovery_event = autils.wait_for_event(
202          other_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
203      peer_id_on_sub = discovery_event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
204
205      # Subscriber: send message to peer (Publisher - so it knows our address)
206      other_dut.droid.wifiAwareSendMessage(
207          s_disc_id, peer_id_on_sub,
208          self.get_next_msg_id(), "ping", aconsts.MAX_TX_RETRIES)
209      autils.wait_for_event(other_dut, aconsts.SESSION_CB_ON_MESSAGE_SENT)
210
211      # Publisher: wait for received message
212      pub_rx_msg_event = autils.wait_for_event(
213          dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED)
214      peer_id_on_pub = pub_rx_msg_event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
215
216      # publisher (responder): request network
217      p_req_key = autils.request_network(
218          dut,
219          dut.droid.wifiAwareCreateNetworkSpecifier(p_disc_id, peer_id_on_pub))
220
221      # subscriber (initiator): request network
222      s_req_key = autils.request_network(
223          other_dut,
224          other_dut.droid.wifiAwareCreateNetworkSpecifier(
225              s_disc_id, peer_id_on_sub))
226
227      # wait for network (or not - on the last iteration)
228      if i != max_ndp:
229        p_net_event = autils.wait_for_event_with_keys(
230            dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
231            (cconsts.NETWORK_CB_KEY_EVENT,
232             cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
233            (cconsts.NETWORK_CB_KEY_ID, p_req_key))
234        s_net_event = autils.wait_for_event_with_keys(
235            other_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
236            (cconsts.NETWORK_CB_KEY_EVENT,
237             cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
238            (cconsts.NETWORK_CB_KEY_ID, s_req_key))
239
240        p_aware_if = p_net_event['data'][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
241        s_aware_if = s_net_event['data'][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
242        self.log.info('Interface names: p=%s, s=%s', p_aware_if, s_aware_if)
243
244        p_ipv6 = dut.droid.connectivityGetLinkLocalIpv6Address(
245            p_aware_if).split('%')[0]
246        s_ipv6 = other_dut.droid.connectivityGetLinkLocalIpv6Address(
247            s_aware_if).split('%')[0]
248        self.log.info('Interface addresses (IPv6): p=%s, s=%s', p_ipv6, s_ipv6)
249      else:
250        autils.fail_on_event_with_keys(
251            dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
252            (cconsts.NETWORK_CB_KEY_EVENT,
253             cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
254            (cconsts.NETWORK_CB_KEY_ID, p_req_key))
255        autils.fail_on_event_with_keys(
256            other_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
257            (cconsts.NETWORK_CB_KEY_EVENT,
258             cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
259            (cconsts.NETWORK_CB_KEY_ID, s_req_key))
260
261    asserts.assert_true(num_peer_devices > max_ndp,
262                        'Needed %d devices to run the test, have %d' %
263                        (max_ndp + 2, len(self.android_devices)))
264