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