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