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 string 18import time 19 20from acts import asserts 21from acts.test_decorators import test_tracker_info 22from acts.test_utils.wifi.aware import aware_const as aconsts 23from acts.test_utils.wifi.aware import aware_test_utils as autils 24from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest 25 26 27class MessageTest(AwareBaseTest): 28 """Set of tests for Wi-Fi Aware L2 (layer 2) message exchanges.""" 29 30 # configuration parameters used by tests 31 PAYLOAD_SIZE_MIN = 0 32 PAYLOAD_SIZE_TYPICAL = 1 33 PAYLOAD_SIZE_MAX = 2 34 35 NUM_MSGS_NO_QUEUE = 10 36 NUM_MSGS_QUEUE_DEPTH_MULT = 2 # number of messages = mult * queue depth 37 38 def __init__(self, controllers): 39 AwareBaseTest.__init__(self, controllers) 40 41 def create_msg(self, caps, payload_size, id): 42 """Creates a message string of the specified size containing the input id. 43 44 Args: 45 caps: Device capabilities. 46 payload_size: The size of the message to create - min (null or empty 47 message), typical, max (based on device capabilities). Use 48 the PAYLOAD_SIZE_xx constants. 49 id: Information to include in the generated message (or None). 50 51 Returns: A string of the requested size, optionally containing the id. 52 """ 53 if payload_size == self.PAYLOAD_SIZE_MIN: 54 # arbitrarily return a None or an empty string (equivalent messages) 55 return None if id % 2 == 0 else "" 56 elif payload_size == self.PAYLOAD_SIZE_TYPICAL: 57 return "*** ID=%d ***" % id + string.ascii_uppercase 58 else: # PAYLOAD_SIZE_MAX 59 return "*** ID=%4d ***" % id + "M" * ( 60 caps[aconsts.CAP_MAX_SERVICE_SPECIFIC_INFO_LEN] - 15) 61 62 def create_config(self, is_publish, extra_diff=None): 63 """Create a base configuration based on input parameters. 64 65 Args: 66 is_publish: True for publish, False for subscribe sessions. 67 extra_diff: String to add to service name: allows differentiating 68 discovery sessions. 69 70 Returns: 71 publish discovery configuration object. 72 """ 73 config = {} 74 if is_publish: 75 config[aconsts. 76 DISCOVERY_KEY_DISCOVERY_TYPE] = aconsts.PUBLISH_TYPE_UNSOLICITED 77 else: 78 config[ 79 aconsts.DISCOVERY_KEY_DISCOVERY_TYPE] = aconsts.SUBSCRIBE_TYPE_PASSIVE 80 config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = "GoogleTestServiceX" + ( 81 extra_diff if extra_diff is not None else "") 82 return config 83 84 def prep_message_exchange(self, extra_diff=None): 85 """Creates a discovery session (publish and subscribe), and waits for 86 service discovery - at that point the sessions are ready for message 87 exchange. 88 89 Args: 90 extra_diff: String to add to service name: allows differentiating 91 discovery sessions. 92 """ 93 p_dut = self.android_devices[0] 94 p_dut.pretty_name = "Publisher" 95 s_dut = self.android_devices[1] 96 s_dut.pretty_name = "Subscriber" 97 98 # if differentiating (multiple) sessions then should decorate events with id 99 use_id = extra_diff is not None 100 101 # Publisher+Subscriber: attach and wait for confirmation 102 p_id = p_dut.droid.wifiAwareAttach(False, None, use_id) 103 autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED 104 if not use_id else autils.decorate_event( 105 aconsts.EVENT_CB_ON_ATTACHED, p_id)) 106 time.sleep(self.device_startup_offset) 107 s_id = s_dut.droid.wifiAwareAttach(False, None, use_id) 108 autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED 109 if not use_id else autils.decorate_event( 110 aconsts.EVENT_CB_ON_ATTACHED, s_id)) 111 112 # Publisher: start publish and wait for confirmation 113 p_disc_id = p_dut.droid.wifiAwarePublish(p_id, 114 self.create_config( 115 True, extra_diff=extra_diff), 116 use_id) 117 autils.wait_for_event(p_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED 118 if not use_id else autils.decorate_event( 119 aconsts.SESSION_CB_ON_PUBLISH_STARTED, p_disc_id)) 120 121 # Subscriber: start subscribe and wait for confirmation 122 s_disc_id = s_dut.droid.wifiAwareSubscribe( 123 s_id, self.create_config(False, extra_diff=extra_diff), use_id) 124 autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED 125 if not use_id else autils.decorate_event( 126 aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, 127 s_disc_id)) 128 129 # Subscriber: wait for service discovery 130 discovery_event = autils.wait_for_event( 131 s_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED 132 if not use_id else autils.decorate_event( 133 aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, s_disc_id)) 134 peer_id_on_sub = discovery_event["data"][aconsts.SESSION_CB_KEY_PEER_ID] 135 136 return { 137 "p_dut": p_dut, 138 "s_dut": s_dut, 139 "p_id": p_id, 140 "s_id": s_id, 141 "p_disc_id": p_disc_id, 142 "s_disc_id": s_disc_id, 143 "peer_id_on_sub": peer_id_on_sub 144 } 145 146 def run_message_no_queue(self, payload_size): 147 """Validate L2 message exchange between publisher & subscriber with no 148 queueing - i.e. wait for an ACK on each message before sending the next 149 message. 150 151 Args: 152 payload_size: min, typical, or max (PAYLOAD_SIZE_xx). 153 """ 154 discovery_info = self.prep_message_exchange() 155 p_dut = discovery_info["p_dut"] 156 s_dut = discovery_info["s_dut"] 157 p_disc_id = discovery_info["p_disc_id"] 158 s_disc_id = discovery_info["s_disc_id"] 159 peer_id_on_sub = discovery_info["peer_id_on_sub"] 160 161 for i in range(self.NUM_MSGS_NO_QUEUE): 162 msg = self.create_msg(s_dut.aware_capabilities, payload_size, i) 163 msg_id = self.get_next_msg_id() 164 s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub, msg_id, msg, 165 0) 166 tx_event = autils.wait_for_event(s_dut, 167 aconsts.SESSION_CB_ON_MESSAGE_SENT) 168 rx_event = autils.wait_for_event(p_dut, 169 aconsts.SESSION_CB_ON_MESSAGE_RECEIVED) 170 asserts.assert_equal(msg_id, 171 tx_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_ID], 172 "Subscriber -> Publisher message ID corrupted") 173 autils.assert_equal_strings( 174 msg, rx_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], 175 "Subscriber -> Publisher message %d corrupted" % i) 176 177 peer_id_on_pub = rx_event["data"][aconsts.SESSION_CB_KEY_PEER_ID] 178 for i in range(self.NUM_MSGS_NO_QUEUE): 179 msg = self.create_msg(s_dut.aware_capabilities, payload_size, 1000 + i) 180 msg_id = self.get_next_msg_id() 181 p_dut.droid.wifiAwareSendMessage(p_disc_id, peer_id_on_pub, msg_id, msg, 182 0) 183 tx_event = autils.wait_for_event(p_dut, 184 aconsts.SESSION_CB_ON_MESSAGE_SENT) 185 rx_event = autils.wait_for_event(s_dut, 186 aconsts.SESSION_CB_ON_MESSAGE_RECEIVED) 187 asserts.assert_equal(msg_id, 188 tx_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_ID], 189 "Publisher -> Subscriber message ID corrupted") 190 autils.assert_equal_strings( 191 msg, rx_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], 192 "Publisher -> Subscriber message %d corrupted" % i) 193 194 # verify there are no more events 195 time.sleep(autils.EVENT_TIMEOUT) 196 autils.verify_no_more_events(p_dut, timeout=0) 197 autils.verify_no_more_events(s_dut, timeout=0) 198 199 def wait_for_messages(self, tx_msgs, tx_msg_ids, tx_disc_id, rx_disc_id, 200 tx_dut, rx_dut, are_msgs_empty=False): 201 """Validate that all expected messages are transmitted correctly and 202 received as expected. Method is called after the messages are sent into 203 the transmission queue. 204 205 Note: that message can be transmitted and received out-of-order (which is 206 acceptable and the method handles that correctly). 207 208 Args: 209 tx_msgs: dictionary of transmitted messages 210 tx_msg_ids: dictionary of transmitted message ids 211 tx_disc_id: transmitter discovery session id (None for no decoration) 212 rx_disc_id: receiver discovery session id (None for no decoration) 213 tx_dut: transmitter device 214 rx_dut: receiver device 215 are_msgs_empty: True if the messages are None or empty (changes dup detection) 216 217 Returns: the peer ID from any of the received messages 218 """ 219 # peer id on receiver 220 peer_id_on_rx = None 221 222 # wait for all messages to be transmitted 223 still_to_be_tx = len(tx_msg_ids) 224 while still_to_be_tx != 0: 225 tx_event = autils.wait_for_event( 226 tx_dut, aconsts.SESSION_CB_ON_MESSAGE_SENT 227 if tx_disc_id is None else autils.decorate_event( 228 aconsts.SESSION_CB_ON_MESSAGE_SENT, tx_disc_id)) 229 tx_msg_id = tx_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_ID] 230 tx_msg_ids[tx_msg_id] = tx_msg_ids[tx_msg_id] + 1 231 if tx_msg_ids[tx_msg_id] == 1: 232 still_to_be_tx = still_to_be_tx - 1 233 234 # check for any duplicate transmit notifications 235 asserts.assert_equal( 236 len(tx_msg_ids), 237 sum(tx_msg_ids.values()), 238 "Duplicate transmit message IDs: %s" % tx_msg_ids) 239 240 # wait for all messages to be received 241 still_to_be_rx = len(tx_msg_ids) 242 while still_to_be_rx != 0: 243 rx_event = autils.wait_for_event( 244 rx_dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED 245 if rx_disc_id is None else autils.decorate_event( 246 aconsts.SESSION_CB_ON_MESSAGE_RECEIVED, rx_disc_id)) 247 peer_id_on_rx = rx_event["data"][aconsts.SESSION_CB_KEY_PEER_ID] 248 if are_msgs_empty: 249 still_to_be_rx = still_to_be_rx - 1 250 else: 251 rx_msg = rx_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING] 252 asserts.assert_true( 253 rx_msg in tx_msgs, 254 "Received a message we did not send!? -- '%s'" % rx_msg) 255 tx_msgs[rx_msg] = tx_msgs[rx_msg] + 1 256 if tx_msgs[rx_msg] == 1: 257 still_to_be_rx = still_to_be_rx - 1 258 259 # check for any duplicate received messages 260 if not are_msgs_empty: 261 asserts.assert_equal( 262 len(tx_msgs), 263 sum(tx_msgs.values()), "Duplicate transmit messages: %s" % tx_msgs) 264 265 return peer_id_on_rx 266 267 def run_message_with_queue(self, payload_size): 268 """Validate L2 message exchange between publisher & subscriber with 269 queueing - i.e. transmit all messages and then wait for ACKs. 270 271 Args: 272 payload_size: min, typical, or max (PAYLOAD_SIZE_xx). 273 """ 274 discovery_info = self.prep_message_exchange() 275 p_dut = discovery_info["p_dut"] 276 s_dut = discovery_info["s_dut"] 277 p_disc_id = discovery_info["p_disc_id"] 278 s_disc_id = discovery_info["s_disc_id"] 279 peer_id_on_sub = discovery_info["peer_id_on_sub"] 280 281 msgs = {} 282 msg_ids = {} 283 for i in range( 284 self.NUM_MSGS_QUEUE_DEPTH_MULT * 285 s_dut.aware_capabilities[aconsts.CAP_MAX_QUEUED_TRANSMIT_MESSAGES]): 286 msg = self.create_msg(s_dut.aware_capabilities, payload_size, i) 287 msg_id = self.get_next_msg_id() 288 msgs[msg] = 0 289 msg_ids[msg_id] = 0 290 s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub, msg_id, msg, 291 0) 292 peer_id_on_pub = self.wait_for_messages( 293 msgs, msg_ids, None, None, s_dut, p_dut, 294 payload_size == self.PAYLOAD_SIZE_MIN) 295 296 msgs = {} 297 msg_ids = {} 298 for i in range( 299 self.NUM_MSGS_QUEUE_DEPTH_MULT * 300 p_dut.aware_capabilities[aconsts.CAP_MAX_QUEUED_TRANSMIT_MESSAGES]): 301 msg = self.create_msg(p_dut.aware_capabilities, payload_size, 1000 + i) 302 msg_id = self.get_next_msg_id() 303 msgs[msg] = 0 304 msg_ids[msg_id] = 0 305 p_dut.droid.wifiAwareSendMessage(p_disc_id, peer_id_on_pub, msg_id, msg, 306 0) 307 self.wait_for_messages(msgs, msg_ids, None, None, p_dut, s_dut, 308 payload_size == self.PAYLOAD_SIZE_MIN) 309 310 # verify there are no more events 311 time.sleep(autils.EVENT_TIMEOUT) 312 autils.verify_no_more_events(p_dut, timeout=0) 313 autils.verify_no_more_events(s_dut, timeout=0) 314 315 def run_message_multi_session_with_queue(self, payload_size): 316 """Validate L2 message exchange between publishers & subscribers with 317 queueing - i.e. transmit all messages and then wait for ACKs. Uses 2 318 discovery sessions running concurrently and validates that messages 319 arrive at the correct destination. 320 321 Args: 322 payload_size: min, typical, or max (PAYLOAD_SIZE_xx) 323 """ 324 discovery_info1 = self.prep_message_exchange(extra_diff="-111") 325 p_dut = discovery_info1["p_dut"] # same for both sessions 326 s_dut = discovery_info1["s_dut"] # same for both sessions 327 p_disc_id1 = discovery_info1["p_disc_id"] 328 s_disc_id1 = discovery_info1["s_disc_id"] 329 peer_id_on_sub1 = discovery_info1["peer_id_on_sub"] 330 331 discovery_info2 = self.prep_message_exchange(extra_diff="-222") 332 p_disc_id2 = discovery_info2["p_disc_id"] 333 s_disc_id2 = discovery_info2["s_disc_id"] 334 peer_id_on_sub2 = discovery_info2["peer_id_on_sub"] 335 336 msgs1 = {} 337 msg_ids1 = {} 338 msgs2 = {} 339 msg_ids2 = {} 340 for i in range( 341 self.NUM_MSGS_QUEUE_DEPTH_MULT * 342 s_dut.aware_capabilities[aconsts.CAP_MAX_QUEUED_TRANSMIT_MESSAGES]): 343 msg1 = self.create_msg(s_dut.aware_capabilities, payload_size, i) 344 msg_id1 = self.get_next_msg_id() 345 msgs1[msg1] = 0 346 msg_ids1[msg_id1] = 0 347 s_dut.droid.wifiAwareSendMessage(s_disc_id1, peer_id_on_sub1, msg_id1, 348 msg1, 0) 349 msg2 = self.create_msg(s_dut.aware_capabilities, payload_size, 100 + i) 350 msg_id2 = self.get_next_msg_id() 351 msgs2[msg2] = 0 352 msg_ids2[msg_id2] = 0 353 s_dut.droid.wifiAwareSendMessage(s_disc_id2, peer_id_on_sub2, msg_id2, 354 msg2, 0) 355 356 peer_id_on_pub1 = self.wait_for_messages( 357 msgs1, msg_ids1, s_disc_id1, p_disc_id1, s_dut, p_dut, 358 payload_size == self.PAYLOAD_SIZE_MIN) 359 peer_id_on_pub2 = self.wait_for_messages( 360 msgs2, msg_ids2, s_disc_id2, p_disc_id2, s_dut, p_dut, 361 payload_size == self.PAYLOAD_SIZE_MIN) 362 363 msgs1 = {} 364 msg_ids1 = {} 365 msgs2 = {} 366 msg_ids2 = {} 367 for i in range( 368 self.NUM_MSGS_QUEUE_DEPTH_MULT * 369 p_dut.aware_capabilities[aconsts.CAP_MAX_QUEUED_TRANSMIT_MESSAGES]): 370 msg1 = self.create_msg(p_dut.aware_capabilities, payload_size, 1000 + i) 371 msg_id1 = self.get_next_msg_id() 372 msgs1[msg1] = 0 373 msg_ids1[msg_id1] = 0 374 p_dut.droid.wifiAwareSendMessage(p_disc_id1, peer_id_on_pub1, msg_id1, 375 msg1, 0) 376 msg2 = self.create_msg(p_dut.aware_capabilities, payload_size, 1100 + i) 377 msg_id2 = self.get_next_msg_id() 378 msgs2[msg2] = 0 379 msg_ids2[msg_id2] = 0 380 p_dut.droid.wifiAwareSendMessage(p_disc_id2, peer_id_on_pub2, msg_id2, 381 msg2, 0) 382 383 self.wait_for_messages(msgs1, msg_ids1, p_disc_id1, s_disc_id1, p_dut, 384 s_dut, payload_size == self.PAYLOAD_SIZE_MIN) 385 self.wait_for_messages(msgs2, msg_ids2, p_disc_id2, s_disc_id2, p_dut, 386 s_dut, payload_size == self.PAYLOAD_SIZE_MIN) 387 388 # verify there are no more events 389 time.sleep(autils.EVENT_TIMEOUT) 390 autils.verify_no_more_events(p_dut, timeout=0) 391 autils.verify_no_more_events(s_dut, timeout=0) 392 393 ############################################################################ 394 395 @test_tracker_info(uuid="a8cd0512-b279-425f-93cf-949ddba22c7a") 396 def test_message_no_queue_min(self): 397 """Functional / Message / No queue 398 - Minimal payload size (None or "") 399 """ 400 self.run_message_no_queue(self.PAYLOAD_SIZE_MIN) 401 402 @test_tracker_info(uuid="2c26170a-5d0a-4cf4-b0b9-56ef03f5dcf4") 403 def test_message_no_queue_typical(self): 404 """Functional / Message / No queue 405 - Typical payload size 406 """ 407 self.run_message_no_queue(self.PAYLOAD_SIZE_TYPICAL) 408 409 @test_tracker_info(uuid="c984860c-b62d-4d9b-8bce-4d894ea3bfbe") 410 def test_message_no_queue_max(self): 411 """Functional / Message / No queue 412 - Max payload size (based on device capabilities) 413 """ 414 self.run_message_no_queue(self.PAYLOAD_SIZE_MAX) 415 416 @test_tracker_info(uuid="3f06de73-31ab-4e0c-bc6f-59abdaf87f4f") 417 def test_message_with_queue_min(self): 418 """Functional / Message / With queue 419 - Minimal payload size (none or "") 420 """ 421 self.run_message_with_queue(self.PAYLOAD_SIZE_MIN) 422 423 @test_tracker_info(uuid="9b7f5bd8-b0b1-479e-8e4b-9db0bb56767b") 424 def test_message_with_queue_typical(self): 425 """Functional / Message / With queue 426 - Typical payload size 427 """ 428 self.run_message_with_queue(self.PAYLOAD_SIZE_TYPICAL) 429 430 @test_tracker_info(uuid="4f9a6dce-3050-4e6a-a143-53592c6c7c28") 431 def test_message_with_queue_max(self): 432 """Functional / Message / With queue 433 - Max payload size (based on device capabilities) 434 """ 435 self.run_message_with_queue(self.PAYLOAD_SIZE_MAX) 436 437 @test_tracker_info(uuid="4cece232-0983-4d6b-90a9-1bb9314b64f0") 438 def test_message_with_multiple_discovery_sessions_typical(self): 439 """Functional / Message / Multiple sessions 440 441 Sets up 2 discovery sessions on 2 devices. Sends a message in each 442 direction on each discovery session and verifies that reaches expected 443 destination. 444 """ 445 self.run_message_multi_session_with_queue(self.PAYLOAD_SIZE_TYPICAL) 446