1#  Copyright (C) 2024 The Android Open Source Project
2#
3#  Licensed under the Apache License, Version 2.0 (the "License");
4#  you may not use this file except in compliance with the License.
5#  You may obtain a copy of the License at
6#
7#       http://www.apache.org/licenses/LICENSE-2.0
8#
9#  Unless required by applicable law or agreed to in writing, software
10#  distributed under the License is distributed on an "AS IS" BASIS,
11#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12#  See the License for the specific language governing permissions and
13#  limitations under the License.
14
15# Lint as: python3
16"""Wi-Fi Aware Discovery test reimplemented in Mobly."""
17import enum
18import logging
19import random
20import string
21import sys
22import time
23from typing import Any, Dict, Union
24
25from aware import aware_lib_utils as autils
26from aware import constants
27from mobly import asserts
28from mobly import base_test
29from mobly import records
30from mobly import test_runner
31from mobly import utils
32from mobly.controllers import android_device
33from mobly.controllers.android_device_lib import callback_handler_v2
34
35RUNTIME_PERMISSIONS = (
36    'android.permission.ACCESS_FINE_LOCATION',
37    'android.permission.ACCESS_COARSE_LOCATION',
38    'android.permission.NEARBY_WIFI_DEVICES',
39)
40PACKAGE_NAME = constants.WIFI_AWARE_SNIPPET_PACKAGE_NAME
41_DEFAULT_TIMEOUT = constants.WAIT_WIFI_STATE_TIME_OUT.total_seconds()
42_MSG_ID_SUB_TO_PUB = random.randint(1000, 5000)
43_MSG_ID_PUB_TO_SUB = random.randint(5001, 9999)
44_MSG_SUB_TO_PUB = "Let's talk [Random Identifier: %s]" % utils.rand_ascii_str(5)
45_MSG_PUB_TO_SUB = 'Ready [Random Identifier: %s]' % utils.rand_ascii_str(5)
46_CALLBACK_NAME = constants.DiscoverySessionCallbackParamsType.CALLBACK_NAME
47_IS_SESSION_INIT = constants.DiscoverySessionCallbackParamsType.IS_SESSION_INIT
48
49# Publish & Subscribe Config keys.
50_PAYLOAD_SIZE_MIN = 0
51_PAYLOAD_SIZE_TYPICAL = 1
52_PAYLOAD_SIZE_MAX = 2
53_PUBLISH_TYPE_UNSOLICITED = 0
54_PUBLISH_TYPE_SOLICITED = 1
55_SUBSCRIBE_TYPE_PASSIVE = 0
56_SUBSCRIBE_TYPE_ACTIVE = 1
57
58
59class WifiAwareDiscoveryTest(base_test.BaseTestClass):
60    """Wi-Fi Aware test class."""
61
62    ads: list[android_device.AndroidDevice]
63    publisher: android_device.AndroidDevice
64    subscriber: android_device.AndroidDevice
65
66    def setup_class(self):
67        # Register two Android devices.
68        self.ads = self.register_controller(android_device, min_number=2)
69        self.publisher = self.ads[0]
70        self.subscriber = self.ads[1]
71
72        def setup_device(device: android_device.AndroidDevice):
73            device.load_snippet(
74                'wifi_aware_snippet', PACKAGE_NAME
75            )
76            for permission in RUNTIME_PERMISSIONS:
77                device.adb.shell(['pm', 'grant', PACKAGE_NAME, permission])
78            asserts.abort_all_if(
79                not device.wifi_aware_snippet.wifiAwareIsAvailable(),
80                f'{device} Wi-Fi Aware is not available.',
81            )
82
83        # Set up devices in parallel.
84        utils.concurrent_exec(
85            setup_device,
86            ((self.publisher,), (self.subscriber,)),
87            max_workers=2,
88            raise_on_exception=True,
89        )
90
91    def setup_test(self):
92        for ad in self.ads:
93            autils.control_wifi(ad, True)
94            aware_avail = ad.wifi_aware_snippet.wifiAwareIsAvailable()
95            if not aware_avail:
96                ad.log.info('Aware not available. Waiting ...')
97                state_handler = ad.wifi_aware_snippet.wifiAwareMonitorStateChange()
98                state_handler.waitAndGet(
99                    constants.WifiAwareBroadcast.WIFI_AWARE_AVAILABLE)
100
101    def teardown_test(self):
102        utils.concurrent_exec(
103            self._teardown_test_on_device,
104            ((self.publisher,), (self.subscriber,)),
105            max_workers=2,
106            raise_on_exception=True,
107        )
108        utils.concurrent_exec(
109            lambda d: d.services.create_output_excerpts_all(
110                self.current_test_info),
111            param_list=[[ad] for ad in self.ads],
112            raise_on_exception=True,
113        )
114
115    def _teardown_test_on_device(self, ad: android_device.AndroidDevice) -> None:
116        ad.wifi_aware_snippet.wifiAwareCloseAllWifiAwareSession()
117        autils.reset_device_parameters(ad)
118        autils.reset_device_statistics(ad)
119
120    def on_fail(self, record: records.TestResult) -> None:
121        android_device.take_bug_reports(self.ads,
122                                        destination =
123                                        self.current_test_info.output_path)
124
125    def _start_attach(self, ad: android_device.AndroidDevice) -> str:
126        """Starts the attach process on the provided device."""
127        handler = ad.wifi_aware_snippet.wifiAwareAttach()
128        attach_event = handler.waitAndGet(
129            event_name=constants.AttachCallBackMethodType.ATTACHED,
130            timeout=_DEFAULT_TIMEOUT,
131        )
132        asserts.assert_true(
133            ad.wifi_aware_snippet.wifiAwareIsSessionAttached(handler.callback_id),
134            f'{ad} attach succeeded, but Wi-Fi Aware session is still null.'
135        )
136        ad.log.info('Attach Wi-Fi Aware session succeeded.')
137        return attach_event.callback_id
138
139    def _send_msg_and_check_received(
140        self,
141        *,
142        sender: android_device.AndroidDevice,
143        sender_aware_session_cb_handler: callback_handler_v2.CallbackHandlerV2,
144        receiver: android_device.AndroidDevice,
145        receiver_aware_session_cb_handler: callback_handler_v2.CallbackHandlerV2,
146        discovery_session: str,
147        peer: int,
148        send_message: str,
149        send_message_id: int,
150    ) -> int:
151        sender.wifi_aware_snippet.wifiAwareSendMessage(
152            discovery_session, peer, send_message_id, send_message
153        )
154        message_send_result = sender_aware_session_cb_handler.waitAndGet(
155            event_name =
156            constants.DiscoverySessionCallbackMethodType.MESSAGE_SEND_RESULT,
157            timeout =_DEFAULT_TIMEOUT,
158        )
159        callback_name = message_send_result.data[
160            constants.DiscoverySessionCallbackParamsType.CALLBACK_NAME
161        ]
162        asserts.assert_equal(
163            callback_name,
164            constants.DiscoverySessionCallbackMethodType.MESSAGE_SEND_SUCCEEDED,
165            f'{sender} failed to send message with an unexpected callback.',
166        )
167        actual_send_message_id = message_send_result.data[
168            constants.DiscoverySessionCallbackParamsType.MESSAGE_ID
169        ]
170        asserts.assert_equal(
171            actual_send_message_id,
172            send_message_id,
173            f'{sender} send message succeeded but message ID mismatched.'
174        )
175        receive_message_event = receiver_aware_session_cb_handler.waitAndGet(
176            event_name = constants.DiscoverySessionCallbackMethodType.MESSAGE_RECEIVED,
177            timeout = _DEFAULT_TIMEOUT,
178        )
179        received_message_raw = receive_message_event.data[
180            constants.WifiAwareSnippetParams.RECEIVED_MESSAGE
181        ]
182        received_message = bytes(received_message_raw).decode('utf-8')
183        asserts.assert_equal(
184            received_message,
185            send_message,
186            f'{receiver} received the message but message content mismatched.'
187        )
188        return receive_message_event.data[
189            constants.WifiAwareSnippetParams.PEER_ID]
190
191    def create_base_config(self,
192                           caps: Dict[str, Union[bool, int, str]],
193                           is_publish: bool,
194                           ptype: Union[int, None],
195                           stype: Union[int, None],
196                           payload_size: int,
197                           ttl: int,
198                           term_ind_on: bool,
199                           null_match: bool) -> Dict[str, Any]:
200        config = {}
201        if is_publish:
202            config[constants.PUBLISH_TYPE] = ptype
203        else:
204            config[constants.SUBSCRIBE_TYPE] = stype
205        config[constants.TTL_SEC] = ttl
206        config[constants.TERMINATE_NOTIFICATION_ENABLED] = term_ind_on
207        if payload_size == _PAYLOAD_SIZE_MIN:
208            config[constants.SERVICE_NAME] = "a"
209            config[constants.SERVICE_SPECIFIC_INFO] = None
210            config[constants.MATCH_FILTER] = []
211        elif payload_size == _PAYLOAD_SIZE_TYPICAL:
212            config[constants.SERVICE_NAME] = "GoogleTestServiceX"
213            if is_publish:
214                config[constants.SERVICE_SPECIFIC_INFO] = string.ascii_letters
215            else:
216                config[constants.SERVICE_SPECIFIC_INFO] = string.ascii_letters[
217                    ::-1]
218            config[constants.MATCH_FILTER] = autils.encode_list(
219                [(10).to_bytes(1, byteorder="big"),"hello there string"
220                 if not null_match else None,bytes(range(40))])
221        else:  # aware_constant.PAYLOAD_SIZE_MAX
222            config[constants.SERVICE_NAME] = "VeryLong" + "X" * (
223                len("maxServiceNameLen") - 8)
224            config[constants.SERVICE_SPECIFIC_INFO] = (
225                "P" if is_publish else "S") * len("maxServiceSpecificInfoLen")
226            mf = autils.construct_max_match_filter(len("maxMatchFilterLen"))
227            if null_match:
228                mf[2] = None
229            config[constants.MATCH_FILTER] = autils.encode_list(mf)
230        return config
231
232    def create_publish_config(self, caps: Dict[str, Union[bool, int, str]],
233                              ptype: int, payload_size: int, ttl: int,
234                              term_ind_on: bool,
235                              null_match: bool) -> Dict[str, Any]:
236        return self.create_base_config(caps, True, ptype, None, payload_size,
237                                       ttl, term_ind_on, null_match)
238
239    def create_subscribe_config(self, caps: Dict[str, Union[bool, int, str]],
240                                stype: int, payload_size: int, ttl: int,
241                                term_ind_on: bool,
242                                null_match: bool) -> Dict[str, Any]:
243        return self.create_base_config(caps, False,  None, stype, payload_size,
244                                       ttl, term_ind_on, null_match)
245
246    def _positive_discovery_logic(self, ptype: int, stype: int,
247                                payload_size: int) -> None:
248        """Utility function for positive discovery test.
249
250        1. Attach both publisher + subscriber to WiFi Aware service.
251        2. Publisher publishes a service.
252        3. Subscriber discoveries service(s) from publisher.
253        4. Exchange messages both publisher + subscriber.
254        5. Update publish/subscribe.
255        6. Terminate publish/subscribe.
256
257        Args:
258            ptype: Publish discovery type.
259            stype: Subscribe discovery type.
260            payload_size: One of PAYLOAD_SIZE_* constants - MIN, TYPICAL, MAX.
261
262        """
263        pid = self._start_attach(self.publisher)
264        sid = self._start_attach(self.subscriber)
265        p_config = self.create_publish_config(
266            self.publisher.wifi_aware_snippet.getCharacteristics(),
267            ptype,
268            payload_size,
269            ttl=0,
270            term_ind_on=False,
271            null_match=False,
272            )
273        p_disc_id = self.publisher.wifi_aware_snippet.wifiAwarePublish(
274            pid, p_config
275            )
276        self.publisher.log.info('Created the publish session.')
277        p_discovery = p_disc_id.waitAndGet(
278            constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT)
279        callback_name = p_discovery.data[_CALLBACK_NAME]
280        asserts.assert_equal(
281            constants.DiscoverySessionCallbackMethodType.PUBLISH_STARTED,
282            callback_name,
283            f'{self.publisher} publish failed, got callback: {callback_name}.',
284            )
285        s_config = self.create_subscribe_config(
286            self.subscriber.wifi_aware_snippet.getCharacteristics(),
287            stype,
288            payload_size,
289            ttl=0,
290            term_ind_on=False,
291            null_match=True,
292            )
293        s_disc_id = self.subscriber.wifi_aware_snippet.wifiAwareSubscribe(
294            sid, s_config
295            )
296        s_discovery = s_disc_id.waitAndGet(
297            constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT)
298        callback_name = s_discovery.data[_CALLBACK_NAME]
299        asserts.assert_equal(
300            constants.DiscoverySessionCallbackMethodType.SUBSCRIBE_STARTED,
301            callback_name,
302            f'{self.subscriber} subscribe failed, got callback: {callback_name}.'
303        )
304        is_session_init = s_discovery.data[_IS_SESSION_INIT]
305        asserts.assert_true(
306            is_session_init,
307            f'{self.subscriber} subscribe succeeded, but null session returned.'
308        )
309        discovered_event = s_disc_id.waitAndGet(
310            constants.DiscoverySessionCallbackMethodType.SERVICE_DISCOVERED)
311        subscriber_peer = discovered_event.data[constants.WifiAwareSnippetParams.PEER_ID]
312        if p_config[constants.SERVICE_SPECIFIC_INFO] == None:
313            p_ssi_1st_disc = "null"
314        else:
315            p_ssi_1st_disc = p_config[constants.SERVICE_SPECIFIC_INFO]
316        s_ssi_1st_disc =  bytes(
317            discovered_event.data[
318                constants.WifiAwareSnippetParams.SERVICE_SPECIFIC_INFO]
319            ).decode("utf-8")
320        asserts.assert_equal(s_ssi_1st_disc, p_ssi_1st_disc,
321                             "Discovery mismatch: service specific info (SSI)")
322        p_filter_list_1 = autils.decode_list(p_config[constants.MATCH_FILTER])
323        s_filter_list_1 = discovered_event.data[
324            constants.WifiAwareSnippetParams.MATCH_FILTER]
325        s_filter_list_1 = [bytes(filter[
326            constants.WifiAwareSnippetParams.MATCH_FILTER_VALUE
327            ]).decode("utf-8")
328                           for filter in s_filter_list_1]
329        s_filter_list_1 = autils.decode_list(s_filter_list_1)
330        asserts.assert_equal(s_filter_list_1,
331                             p_filter_list_1 if ptype == _PUBLISH_TYPE_UNSOLICITED
332                             else  autils.decode_list(s_config[constants.MATCH_FILTER]),
333                             "Discovery mismatch: match filter")
334        # Subscriber sends a message to publisher.
335        publisher_peer = self._send_msg_and_check_received(
336            sender=self.subscriber,
337            sender_aware_session_cb_handler=s_disc_id,
338            receiver=self.publisher,
339            receiver_aware_session_cb_handler=p_disc_id,
340            discovery_session=s_disc_id.callback_id,
341            peer=subscriber_peer,
342            send_message=_MSG_SUB_TO_PUB,
343            send_message_id=_MSG_ID_SUB_TO_PUB,
344            )
345        logging.info(
346            'The subscriber sent a message and the publisher received it.'
347            )
348        # Publisher sends a message to subscriber.
349        self._send_msg_and_check_received(
350            sender=self.publisher,
351            sender_aware_session_cb_handler=p_disc_id,
352            receiver=self.subscriber,
353            receiver_aware_session_cb_handler=s_disc_id,
354            discovery_session=p_disc_id.callback_id,
355            peer=publisher_peer,
356            send_message=_MSG_PUB_TO_SUB,
357            send_message_id=_MSG_ID_PUB_TO_SUB,
358        )
359        logging.info(
360            'The publisher sent a message and the subscriber received it.'
361        )
362        p_config[constants.SERVICE_SPECIFIC_INFO] = "something else"
363        self.publisher.wifi_aware_snippet.wifiAwareUpdatePublish(
364            p_disc_id.callback_id, p_config
365            )
366        p_disc_id.waitAndGet(
367            constants.DiscoverySessionCallbackMethodType.SESSION_CONFIG_UPDATED)
368        discovered_event_1 = s_disc_id.waitAndGet(
369            constants.DiscoverySessionCallbackMethodType.SERVICE_DISCOVERED)
370        p_ssi_2st_disc = p_config[constants.SERVICE_SPECIFIC_INFO]
371        s_ssi_2st_disc = bytes(
372            discovered_event_1.data[
373                constants.WifiAwareSnippetParams.SERVICE_SPECIFIC_INFO]
374        ).decode("utf-8")
375        asserts.assert_equal(s_ssi_2st_disc, p_ssi_2st_disc,
376                             "Discovery mismatch (after pub update): service specific info (SSI")
377        p_filter_list_2 = autils.decode_list(p_config[constants.MATCH_FILTER])
378        s_filter_list_2 = discovered_event_1.data[
379            constants.WifiAwareSnippetParams.MATCH_FILTER]
380        s_filter_list_2 = [bytes(filter[
381            constants.WifiAwareSnippetParams.MATCH_FILTER_VALUE]).decode("utf-8")
382                           for filter in s_filter_list_2]
383        s_filter_list_2 = autils.decode_list(s_filter_list_2)
384        asserts.assert_equal(s_filter_list_2,
385                             p_filter_list_2  if ptype == _PUBLISH_TYPE_UNSOLICITED
386                             else  autils.decode_list(s_config[constants.MATCH_FILTER]),
387                             "Discovery mismatch: match filter")
388        disc_peer_id = discovered_event_1.data[
389            constants.WifiAwareSnippetParams.PEER_ID]
390        asserts.assert_equal(subscriber_peer, disc_peer_id,
391                             "Peer ID changed when publish was updated!?")
392        s_config = self.create_subscribe_config(
393            self.subscriber.wifi_aware_snippet.getCharacteristics(),
394            stype,
395            payload_size,
396            ttl=0,
397            term_ind_on=False,
398            null_match=False,
399            )
400        s_disc_id_1 = self.subscriber.wifi_aware_snippet.wifiAwareUpdateSubscribe(
401            discovered_event_1.callback_id, s_config
402            )
403        s_disc_id.waitAndGet(
404            constants.DiscoverySessionCallbackMethodType.SESSION_CONFIG_UPDATED)
405        self.publisher.wifi_aware_snippet.wifiAwareCloseDiscoverSession(
406            p_disc_id.callback_id)
407        self.subscriber.wifi_aware_snippet.wifiAwareCloseDiscoverSession(
408            s_disc_id.callback_id)
409        p_disc_id.waitAndGet(
410            constants.DiscoverySessionCallbackMethodType.SESSION_TERMINATED)
411        s_disc_id.waitAndGet(
412            constants.DiscoverySessionCallbackMethodType.SESSION_TERMINATED)
413        time.sleep(10)
414        if self.publisher.is_adb_root:
415            # Verify that forbidden callbacks aren't called.
416            autils.validate_forbidden_callbacks(self.publisher, {"4": 0})
417        self.publisher.wifi_aware_snippet.wifiAwareDetach(pid)
418        self.subscriber.wifi_aware_snippet.wifiAwareDetach(sid)
419
420    def verify_discovery_session_term(self, dut, disc_id, config, is_publish,
421                                      term_ind_on):
422        """Utility to verify that the specified discovery session has terminated.
423        (by waiting for the TTL and then attempting to reconfigure).
424
425        Args:
426            dut: device under test
427            disc_id: discovery id for the existing session
428            config: configuration of the existing session
429            is_publish: True if the configuration was publish, False if subscribe
430            term_ind_on: True if a termination indication is expected, False otherwise
431        """
432        # Wait for session termination
433        if term_ind_on:
434            disc_id.waitAndGet(
435                constants.DiscoverySessionCallbackMethodType.SESSION_TERMINATED,
436                timeout = _DEFAULT_TIMEOUT)
437        else:
438            autils.callback_no_response(
439                disc_id,
440                constants.DiscoverySessionCallbackMethodType.SESSION_TERMINATED,
441                timeout = _DEFAULT_TIMEOUT)
442        config[constants.SERVICE_SPECIFIC_INFO] = "something else"
443        if is_publish:
444            dut.wifi_aware_snippet.wifiAwareUpdatePublish(
445            disc_id.callback_id, config
446            )
447        else:
448            dut.wifi_aware_snippet.wifiAwareUpdateSubscribe(
449            disc_id.callback_id, config
450            )
451        if not term_ind_on:
452            disc_id.waitAndGet(
453                constants.DiscoverySessionCallbackMethodType.SESSION_CONFIG_FAILED,
454                timeout =_DEFAULT_TIMEOUT
455                )
456
457    def positive_ttl_test_utility(self, is_publish, ptype, stype, term_ind_on):
458        """Utility which runs a positive discovery session TTL configuration test.
459
460        Iteration 1: Verify session started with TTL
461        Iteration 2: Verify session started without TTL and reconfigured with TTL
462        Iteration 3: Verify session started with (long) TTL and reconfigured with
463                 (short) TTL
464
465        Args:
466            is_publish: True if testing publish, False if testing subscribe
467            ptype: Publish discovery type (used if is_publish is True)
468            stype: Subscribe discovery type (used if is_publish is False)
469            term_ind_on: Configuration of termination indication
470        """
471        SHORT_TTL = 5  # 5 seconds
472        LONG_TTL = 100  # 100 seconds
473        dut = self.ads[0]
474        id = self._start_attach(dut)
475        # Iteration 1: Start discovery session with TTL
476        config = self.create_base_config(
477            dut.wifi_aware_snippet.getCharacteristics(),
478            is_publish,
479            ptype, stype,
480            _PAYLOAD_SIZE_TYPICAL,
481            SHORT_TTL,
482            term_ind_on,
483            False)
484        if is_publish:
485            disc_id = dut.wifi_aware_snippet.wifiAwarePublish(
486                id, config
487                )
488            p_discovery = disc_id.waitAndGet(
489                constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT,
490                timeout=_DEFAULT_TIMEOUT)
491            callback_name = p_discovery.data[_CALLBACK_NAME]
492            asserts.assert_equal(
493                constants.DiscoverySessionCallbackMethodType.PUBLISH_STARTED,
494                callback_name,
495                f'{dut} publish failed, got callback: {callback_name}.',
496                )
497        else:
498            disc_id = dut.wifi_aware_snippet.wifiAwareSubscribe(
499                id, config
500                )
501            s_discovery = disc_id.waitAndGet(
502                constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT,
503                timeout=_DEFAULT_TIMEOUT)
504            callback_name = s_discovery.data[_CALLBACK_NAME]
505            asserts.assert_equal(
506                constants.DiscoverySessionCallbackMethodType.SUBSCRIBE_STARTED,
507                callback_name,
508                f'{dut} subscribe failed, got callback: {callback_name}.',
509                )
510        # Wait for session termination & verify
511        self.verify_discovery_session_term(dut, disc_id, config, is_publish,
512                                           term_ind_on)
513        # Iteration 2: Start a discovery session without TTL
514        config = self.create_base_config(
515            dut.wifi_aware_snippet.getCharacteristics(),
516            is_publish,
517            ptype, stype,
518            _PAYLOAD_SIZE_TYPICAL,
519            0,
520            term_ind_on,
521            False)
522        if is_publish:
523            disc_id = dut.wifi_aware_snippet.wifiAwarePublish(
524                id, config
525                )
526            p_discovery = disc_id.waitAndGet(
527                constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT,
528                timeout=_DEFAULT_TIMEOUT)
529            callback_name = p_discovery.data[_CALLBACK_NAME]
530            asserts.assert_equal(
531                constants.DiscoverySessionCallbackMethodType.PUBLISH_STARTED,
532                callback_name,
533                f'{dut} publish failed, got callback: {callback_name}.',
534                )
535        else:
536            disc_id = dut.wifi_aware_snippet.wifiAwareSubscribe(
537                id, config
538                )
539            s_discovery = disc_id.waitAndGet(
540                constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT,
541                timeout=_DEFAULT_TIMEOUT)
542            callback_name = s_discovery.data[_CALLBACK_NAME]
543            asserts.assert_equal(
544                constants.DiscoverySessionCallbackMethodType.SUBSCRIBE_STARTED,
545                callback_name,
546                f'{dut} subscribe failed, got callback: {callback_name}.',
547                )
548        # Update with a TTL
549        config = self.create_base_config(
550            dut.wifi_aware_snippet.getCharacteristics(),
551            is_publish,
552            ptype, stype,
553            _PAYLOAD_SIZE_TYPICAL,
554            SHORT_TTL,
555            term_ind_on,
556            False)
557        if is_publish:
558            disc_id = dut.wifi_aware_snippet.wifiAwarePublish(
559                id, config
560                )
561            p_discovery = disc_id.waitAndGet(
562                constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT,
563                timeout=_DEFAULT_TIMEOUT)
564            callback_name = p_discovery.data[_CALLBACK_NAME]
565            asserts.assert_equal(
566                constants.DiscoverySessionCallbackMethodType.PUBLISH_STARTED,
567                callback_name,
568                f'{dut} publish failed, got callback: {callback_name}.',
569                )
570        else:
571            disc_id = dut.wifi_aware_snippet.wifiAwareSubscribe(
572                id, config
573                )
574            s_discovery = disc_id.waitAndGet(
575                constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT,
576                timeout=_DEFAULT_TIMEOUT)
577            callback_name = s_discovery.data[_CALLBACK_NAME]
578            asserts.assert_equal(
579                constants.DiscoverySessionCallbackMethodType.SUBSCRIBE_STARTED,
580                callback_name,
581                f'{dut} subscribe failed, got callback: {callback_name}.',
582                )
583        # Wait for session termination & verify
584        self.verify_discovery_session_term(dut, disc_id, config, is_publish,
585                                           term_ind_on)
586        # Iteration 3: Start a discovery session with (long) TTL
587        config = self.create_base_config(
588            dut.wifi_aware_snippet.getCharacteristics(),
589            is_publish,
590            ptype, stype,
591            _PAYLOAD_SIZE_TYPICAL,
592            LONG_TTL,
593            term_ind_on,
594            False)
595        if is_publish:
596            disc_id = dut.wifi_aware_snippet.wifiAwarePublish(
597                id, config
598                )
599            p_discovery = disc_id.waitAndGet(
600                constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT,
601                timeout=_DEFAULT_TIMEOUT)
602            callback_name = p_discovery.data[_CALLBACK_NAME]
603            asserts.assert_equal(
604                constants.DiscoverySessionCallbackMethodType.PUBLISH_STARTED,
605                callback_name,
606                f'{dut} publish failed, got callback: {callback_name}.',
607                )
608        else:
609            disc_id = dut.wifi_aware_snippet.wifiAwareSubscribe(
610                id, config
611                )
612            s_discovery = disc_id.waitAndGet(
613                constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT,
614                timeout=_DEFAULT_TIMEOUT)
615            callback_name = s_discovery.data[_CALLBACK_NAME]
616            asserts.assert_equal(
617                constants.DiscoverySessionCallbackMethodType.SUBSCRIBE_STARTED,
618                callback_name,
619                f'{dut} subscribe failed, got callback: {callback_name}.',
620                )
621        # Update with a TTL
622        config = self.create_base_config(
623            dut.wifi_aware_snippet.getCharacteristics(),
624            is_publish,
625            ptype, stype,
626            _PAYLOAD_SIZE_TYPICAL,
627            SHORT_TTL,
628            term_ind_on,
629            False)
630        if is_publish:
631            disc_id = dut.wifi_aware_snippet.wifiAwarePublish(
632                id, config
633                )
634            p_discovery = disc_id.waitAndGet(
635                constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT,
636                timeout=_DEFAULT_TIMEOUT)
637            callback_name = p_discovery.data[_CALLBACK_NAME]
638            asserts.assert_equal(
639                constants.DiscoverySessionCallbackMethodType.PUBLISH_STARTED,
640                callback_name,
641                f'{dut} publish failed, got callback: {callback_name}.',
642                )
643        else:
644            disc_id = dut.wifi_aware_snippet.wifiAwareSubscribe(
645                id, config
646                )
647            s_discovery = disc_id.waitAndGet(
648                constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT,
649                timeout=_DEFAULT_TIMEOUT)
650            callback_name = s_discovery.data[_CALLBACK_NAME]
651            asserts.assert_equal(
652                constants.DiscoverySessionCallbackMethodType.SUBSCRIBE_STARTED,
653                callback_name,
654                f'{dut} subscribe failed, got callback: {callback_name}.',
655                )
656        # Wait for session termination & verify
657        self.verify_discovery_session_term(dut, disc_id, config, is_publish,
658                                           term_ind_on)
659        # verify that forbidden callbacks aren't called
660        if not term_ind_on:
661             autils.validate_forbidden_callbacks(
662                 dut,{
663                    "2": 0,
664                    "3": 0})
665
666    def discovery_mismatch_test_utility(self,
667                                        is_expected_to_pass,
668                                        p_type,
669                                        s_type,
670                                        p_service_name=None,
671                                        s_service_name=None,
672                                        p_mf_1=None,
673                                        s_mf_1=None):
674        """Utility which runs the negative discovery test for mismatched service
675        configs.
676
677        Args:
678            is_expected_to_pass: True if positive test, False if negative
679            p_type: Publish discovery type
680            s_type: Subscribe discovery type
681            p_service_name: Publish service name (or None to leave unchanged)
682            s_service_name: Subscribe service name (or None to leave unchanged)
683            p_mf_1: Publish match filter element [1] (or None to leave unchanged)
684            s_mf_1: Subscribe match filter element [1] (or None to leave unchanged)
685        """
686        p_dut = self.ads[0]
687        s_dut = self.ads[1]
688        # create configurations
689        p_config = self.create_publish_config(
690            p_dut.wifi_aware_snippet.getCharacteristics(),
691            p_type,
692            _PAYLOAD_SIZE_TYPICAL,
693            ttl=0,
694            term_ind_on=False,
695            null_match=False)
696        if p_service_name is not None:
697            p_config[constants.SERVICE_NAME] = p_service_name
698        if p_mf_1 is not None:
699            # p_config[constants.MATCH_FILTER] = p_mf_1.encode("utf-8")
700            p_config[constants.MATCH_FILTER] = autils.encode_list(
701              [(10).to_bytes(1, byteorder="big"), p_mf_1 , bytes(range(40))])
702        s_config = self.create_subscribe_config(
703            s_dut.wifi_aware_snippet.getCharacteristics(),
704            s_type,
705            _PAYLOAD_SIZE_TYPICAL,
706            ttl=0,
707            term_ind_on=False,
708            null_match=False)
709        if s_service_name is not None:
710            s_config[constants.SERVICE_NAME] = s_service_name
711        if s_mf_1 is not None:
712            s_config[constants.MATCH_FILTER] = autils.encode_list(
713              [(10).to_bytes(1, byteorder="big"), s_mf_1 , bytes(range(40))])
714        p_id = self._start_attach(p_dut)
715        s_id = self._start_attach(s_dut)
716        # Publisher: start publish and wait for confirmation
717        p_disc_id = p_dut.wifi_aware_snippet.wifiAwarePublish(
718                p_id, p_config
719                )
720        p_dut.log.info('Created the publish session.')
721        p_discovery = p_disc_id.waitAndGet(
722            constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT)
723        callback_name = p_discovery.data[_CALLBACK_NAME]
724        asserts.assert_equal(
725            constants.DiscoverySessionCallbackMethodType.PUBLISH_STARTED,
726            callback_name,
727            f'{p_dut} publish failed, got callback: {callback_name}.',
728            )
729        s_disc_id = s_dut.wifi_aware_snippet.wifiAwareSubscribe(
730                s_id, s_config
731                )
732        s_discovery = s_disc_id.waitAndGet(
733                constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT,
734                timeout=_DEFAULT_TIMEOUT)
735        callback_name = s_discovery.data[_CALLBACK_NAME]
736        asserts.assert_equal(
737            constants.DiscoverySessionCallbackMethodType.SUBSCRIBE_STARTED,
738            callback_name,
739            f'{s_dut} subscribe failed, got callback: {callback_name}.',
740            )
741        # Subscriber: fail on service discovery
742        if is_expected_to_pass:
743            s_disc_id.waitAndGet(
744                constants.DiscoverySessionCallbackMethodType.SERVICE_DISCOVERED)
745        else:
746            autils.callback_no_response(
747                s_disc_id,
748                constants.DiscoverySessionCallbackMethodType.SERVICE_DISCOVERED,
749                timeout = _DEFAULT_TIMEOUT)
750        # Publisher+Subscriber: Terminate sessions
751        p_dut.wifi_aware_snippet.wifiAwareCloseDiscoverSession(
752            p_disc_id.callback_id)
753        s_dut.wifi_aware_snippet.wifiAwareCloseDiscoverSession(
754            s_disc_id.callback_id)
755        p_disc_id.waitAndGet(
756            constants.DiscoverySessionCallbackMethodType.SESSION_TERMINATED)
757        s_disc_id.waitAndGet(
758            constants.DiscoverySessionCallbackMethodType.SESSION_TERMINATED)
759
760    def create_discovery_pair(
761        self, p_dut, s_dut, p_config, s_config, msg_id=None):
762        """Creates a discovery session (publish and subscribe), and waits for
763        service discovery - at that point the sessions are connected and ready for
764        further messaging of data-path setup.
765
766        Args:
767            p_dut: Device to use as publisher.
768            s_dut: Device to use as subscriber.
769            p_config: Publish configuration.
770            s_config: Subscribe configuration.
771            device_startup_offset: Number of seconds to offset the enabling of NAN on
772                                   the two devices.
773            msg_id: Controls whether a message is sent from Subscriber to Publisher
774            (so that publisher has the sub's peer ID). If None then not sent,
775            otherwise should be an int for the message id.
776        Returns: variable size list of:
777            p_id: Publisher attach session id
778            s_id: Subscriber attach session id
779            p_disc_id: Publisher discovery session id
780            s_disc_id: Subscriber discovery session id
781            peer_id_on_sub: Peer ID of the Publisher as seen on the Subscriber
782            peer_id_on_pub: Peer ID of the Subscriber as seen on the Publisher. Only
783                            included if |msg_id| is not None.
784        """
785
786        p_dut = self.ads[0]
787        s_dut = self.ads[1]
788        # attach and wait for confirmation
789        p_id = self._start_attach(p_dut)
790        s_id = self._start_attach(s_dut)
791        p_disc_id = p_dut.wifi_aware_snippet.wifiAwarePublish(
792                p_id, p_config
793                )
794        p_dut.log.info('Created the publish session.')
795        p_discovery = p_disc_id.waitAndGet(
796            constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT)
797        callback_name = p_discovery.data[_CALLBACK_NAME]
798        asserts.assert_equal(
799            constants.DiscoverySessionCallbackMethodType.PUBLISH_STARTED,
800            callback_name,
801            f'{p_dut} publish failed, got callback: {callback_name}.',
802            )
803        # Subscriber: start subscribe and wait for confirmation
804        s_disc_id = s_dut.wifi_aware_snippet.wifiAwareSubscribe(
805            s_id, s_config
806            )
807        s_dut.log.info('Created the subscribe session.')
808        s_discovery = s_disc_id.waitAndGet(
809            constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT)
810        callback_name = s_discovery.data[_CALLBACK_NAME]
811        asserts.assert_equal(
812            constants.DiscoverySessionCallbackMethodType.SUBSCRIBE_STARTED,
813            callback_name,
814            f'{s_dut} subscribe failed, got callback: {callback_name}.',
815            )
816        # Subscriber: wait for service discovery
817        discovery_event = s_disc_id.waitAndGet(
818            constants.DiscoverySessionCallbackMethodType.SERVICE_DISCOVERED)
819        peer_id_on_sub = discovery_event.data[constants.WifiAwareSnippetParams.PEER_ID]
820        # Optionally send a message from Subscriber to Publisher
821        if msg_id is not None:
822            ping_msg = 'PING'
823            # Subscriber: send message to peer (Publisher)
824            s_dut.sender.wifi_aware_snippet.wifiAwareSendMessage(
825                s_disc_id, peer_id_on_sub, _MSG_ID_SUB_TO_PUB, ping_msg
826                )
827            message_send_result = s_disc_id.waitAndGet(
828                event_name =
829                constants.DiscoverySessionCallbackMethodType.MESSAGE_SEND_RESULT,
830                timeout =_DEFAULT_TIMEOUT,
831                )
832            actual_send_message_id = message_send_result.data[
833            constants.DiscoverySessionCallbackParamsType.MESSAGE_ID
834            ]
835            asserts.assert_equal(
836                actual_send_message_id,
837                _MSG_ID_SUB_TO_PUB,
838                f'{s_dut} send message succeeded but message ID mismatched.'
839                )
840            pub_rx_msg_event = p_disc_id.waitAndGet(
841                event_name = constants.DiscoverySessionCallbackMethodType.MESSAGE_RECEIVED,
842                timeout = _DEFAULT_TIMEOUT,
843                )
844            peer_id_on_pub = pub_rx_msg_event.data[constants.WifiAwareSnippetParams.PEER_ID]
845            received_message_raw = pub_rx_msg_event.data[
846                constants.WifiAwareSnippetParams.RECEIVED_MESSAGE
847                ]
848            received_message = bytes(received_message_raw).decode('utf-8')
849            asserts.assert_equal(
850                received_message,
851                ping_msg,
852                f'{p_dut} Subscriber -> Publisher message corrupted.'
853                )
854            return p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub, peer_id_on_pub
855        return p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub
856
857    def exchange_messages(self, p_dut, p_disc_id, s_dut, s_disc_id, peer_id_on_sub, session_name):
858        """
859        Exchange message between Publisher and Subscriber on target discovery session
860
861        Args:
862            p_dut: Publisher device
863            p_disc_id: Publish discovery session id
864            s_dut: Subscriber device
865            s_disc_id: Subscribe discovery session id
866            peer_id_on_sub: Peer ID of the Publisher as seen on the Subscriber
867            session_name: dictionary of discovery session name base on role("pub" or "sub")
868                          {role: {disc_id: name}}
869        """
870
871        msg_template = "Hello {} from {} !"
872        # Message send from Subscriber to Publisher
873        s_to_p_msg = msg_template.format(session_name["pub"][p_disc_id],
874                                         session_name["sub"][s_disc_id])
875        publisher_peer = self._send_msg_and_check_received(
876            sender = s_dut,
877            sender_aware_session_cb_handler= s_disc_id,
878            receiver = p_dut,
879            receiver_aware_session_cb_handler= p_disc_id,
880            discovery_session = s_disc_id.callback_id,
881            peer=peer_id_on_sub,
882            send_message =s_to_p_msg,
883            send_message_id = _MSG_ID_SUB_TO_PUB,
884            )
885        logging.info(
886            'The subscriber sent a message and the publisher received it.'
887            )
888
889        # Publisher sends a message to subscriber.
890        p_to_s_msg = msg_template.format(session_name["sub"][s_disc_id],
891                                         session_name["pub"][p_disc_id])
892        self._send_msg_and_check_received(
893            sender=p_dut,
894            sender_aware_session_cb_handler=p_disc_id,
895            receiver=s_dut,
896            receiver_aware_session_cb_handler=s_disc_id,
897            discovery_session=p_disc_id.callback_id,
898            peer=publisher_peer,
899            send_message=p_to_s_msg,
900            send_message_id=_MSG_ID_PUB_TO_SUB,
901        )
902        logging.info(
903            'The publisher sent a message and the subscriber received it.'
904        )
905
906    def run_multiple_concurrent_services(self, type_x, type_y):
907        """Validate same service name with multiple service specific info on publisher
908        and subscriber can see all service
909
910        - p_dut running Publish X and Y
911        - s_dut running subscribe A and B
912        - subscribe A find X and Y
913        - subscribe B find X and Y
914
915        Message exchanges:
916            - A to X and X to A
917            - B to X and X to B
918            - A to Y and Y to A
919            - B to Y and Y to B
920
921        Note: test requires that publisher device support 2 publish sessions concurrently,
922        and subscriber device support 2 subscribe sessions concurrently.
923        The test will be skipped if the devices are not capable.
924
925        Args:
926            type_x, type_y: A list of [ptype, stype] of the publish and subscribe
927                      types for services X and Y respectively.
928        """
929
930        p_dut = self.ads[0]
931        s_dut = self.ads[1]
932        X_SERVICE_NAME = "ServiceXXX"
933        Y_SERVICE_NAME = "ServiceYYY"
934        asserts.skip_if(
935            autils.get_aware_capabilities(p_dut)["maxPublishes"] < 2
936            or autils.get_aware_capabilities(s_dut)["maxPublishes"] < 2
937            ,"Devices do not support 2 publish sessions"
938            )
939        # attach and wait for confirmation
940        p_id = self._start_attach(p_dut)
941        s_id = self._start_attach(s_dut)
942        # DUT1 & DUT2: start publishing both X & Y services and wait for
943        # confirmations
944        dut1_x_pid = p_dut.wifi_aware_snippet.wifiAwarePublish(
945            p_id, autils.create_discovery_config(X_SERVICE_NAME, type_x[0], None)
946                )
947        p_dut.log.info('Created the DUT1 X publish session.')
948        p_discovery = dut1_x_pid.waitAndGet(
949            constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT)
950        callback_name = p_discovery.data[_CALLBACK_NAME]
951        asserts.assert_equal(
952            constants.DiscoverySessionCallbackMethodType.PUBLISH_STARTED,
953            callback_name,
954            f'{p_dut} DUT1 X publish failed, got callback: {callback_name}.',
955            )
956        dut1_y_pid = p_dut.wifi_aware_snippet.wifiAwarePublish(
957                p_id, autils.create_discovery_config(Y_SERVICE_NAME, type_y[0], None)
958                )
959        p_dut.log.info('Created the DUT1 Y publish session.')
960        p_discovery = dut1_y_pid.waitAndGet(
961            constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT)
962        callback_name = p_discovery.data[_CALLBACK_NAME]
963        asserts.assert_equal(
964            constants.DiscoverySessionCallbackMethodType.PUBLISH_STARTED,
965            callback_name,
966            f'{p_dut} DUT1 Y publish failed, got callback: {callback_name}.',
967            )
968        dut2_x_pid = s_dut.wifi_aware_snippet.wifiAwarePublish(
969                s_id, autils.create_discovery_config(X_SERVICE_NAME, type_x[0], None)
970                )
971        s_dut.log.info('Created the DUT2 X publish session.')
972        p_discovery = dut2_x_pid.waitAndGet(
973            constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT)
974        callback_name = p_discovery.data[_CALLBACK_NAME]
975        asserts.assert_equal(
976            constants.DiscoverySessionCallbackMethodType.PUBLISH_STARTED,
977            callback_name,
978            f'{s_dut} DUT2 X publish failed, got callback: {callback_name}.',
979            )
980        dut2_y_pid = s_dut.wifi_aware_snippet.wifiAwarePublish(
981                s_id, autils.create_discovery_config(Y_SERVICE_NAME, type_y[0], None)
982                )
983        s_dut.log.info('Created the DUT2 Y publish session.')
984        p_discovery = dut2_y_pid.waitAndGet(
985            constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT)
986        callback_name = p_discovery.data[_CALLBACK_NAME]
987        asserts.assert_equal(
988            constants.DiscoverySessionCallbackMethodType.PUBLISH_STARTED,
989            callback_name,
990            f'{s_dut} DUT1 Y publish failed, got callback: {callback_name}.',
991            )
992        # DUT1: start subscribing for X
993        dut1_x_sid = p_dut.wifi_aware_snippet.wifiAwareSubscribe(
994            p_id, autils.create_discovery_config(X_SERVICE_NAME, None, type_x[1])
995            )
996        p_dut.log.info('Created the DUT1 X subscribe session.')
997        s_discovery = dut1_x_sid.waitAndGet(
998                constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT,
999                timeout=_DEFAULT_TIMEOUT)
1000        callback_name = s_discovery.data[_CALLBACK_NAME]
1001        asserts.assert_equal(
1002            constants.DiscoverySessionCallbackMethodType.SUBSCRIBE_STARTED,
1003            callback_name,
1004            f'{p_dut} DUT1 X subscribe failed, got callback: {callback_name}.',
1005            )
1006        # DUT1: start subscribing for Y
1007        dut1_y_sid = p_dut.wifi_aware_snippet.wifiAwareSubscribe(
1008                p_id, autils.create_discovery_config(Y_SERVICE_NAME, None, type_y[1])
1009                )
1010        p_dut.log.info('Created the DUT1 Y subscribe session.')
1011        s_discovery = dut1_y_sid.waitAndGet(
1012            constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT)
1013        callback_name = s_discovery.data[_CALLBACK_NAME]
1014        asserts.assert_equal(
1015            constants.DiscoverySessionCallbackMethodType.SUBSCRIBE_STARTED,
1016            callback_name,
1017            f'{p_dut} DUT1 Y subscribe failed, got callback: {callback_name}.',
1018            )
1019        # DUT2: start subscribing for X
1020        dut2_x_sid = s_dut.wifi_aware_snippet.wifiAwareSubscribe(
1021            s_id, autils.create_discovery_config(X_SERVICE_NAME, None, type_x[1])
1022            )
1023        s_dut.log.info('Created the DUT2 X subscribe session.')
1024        s_discovery = dut2_x_sid.waitAndGet(
1025                constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT,
1026                timeout=_DEFAULT_TIMEOUT)
1027        callback_name = s_discovery.data[_CALLBACK_NAME]
1028        asserts.assert_equal(
1029            constants.DiscoverySessionCallbackMethodType.SUBSCRIBE_STARTED,
1030            callback_name,
1031            f'{s_dut} DUT2 X subscribe failed, got callback: {callback_name}.',
1032            )
1033        # DUT2: start subscribing for Y
1034        dut2_y_sid = s_dut.wifi_aware_snippet.wifiAwareSubscribe(
1035                s_id, autils.create_discovery_config(Y_SERVICE_NAME, None, type_y[1])
1036                )
1037        s_dut.log.info('Created the DUT2 Y subscribe session.')
1038        s_discovery = dut2_y_sid.waitAndGet(
1039            constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT)
1040        callback_name = s_discovery.data[_CALLBACK_NAME]
1041        asserts.assert_equal(
1042            constants.DiscoverySessionCallbackMethodType.SUBSCRIBE_STARTED,
1043            callback_name,
1044            f'{s_dut} DUT2 Y subscribe failed, got callback: {callback_name}.',
1045            )
1046        dut1_x_sid_event = dut1_x_sid.waitAndGet(
1047            constants.DiscoverySessionCallbackMethodType.SERVICE_DISCOVERED)
1048        dut1_peer_id_for_dut2_x = dut1_x_sid_event.data[constants.WifiAwareSnippetParams.PEER_ID]
1049
1050        dut2_y_sid_event = dut2_y_sid.waitAndGet(
1051            constants.DiscoverySessionCallbackMethodType.SERVICE_DISCOVERED)
1052        dut2_peer_id_for_dut1_y = dut2_y_sid_event.data[constants.WifiAwareSnippetParams.PEER_ID]
1053
1054        # DUT1.X send message to DUT2
1055        x_msg = "Hello X on DUT2!"
1056        publisher_peer = self._send_msg_and_check_received(
1057            sender = p_dut,
1058            sender_aware_session_cb_handler= dut1_x_sid,
1059            receiver = s_dut,
1060            receiver_aware_session_cb_handler= dut2_x_pid,
1061            discovery_session = dut1_x_sid.callback_id,
1062            peer=dut1_peer_id_for_dut2_x,
1063            send_message =x_msg,
1064            send_message_id = _MSG_ID_PUB_TO_SUB,
1065            )
1066        logging.info(
1067            'The DUT1.X sent a message and the DUT2 received it.'
1068            )
1069
1070        # DUT2.Y send message to DUT1
1071        y_msg = "Hello Y on DUT1!"
1072        self._send_msg_and_check_received(
1073            sender = s_dut,
1074            sender_aware_session_cb_handler= dut2_y_sid,
1075            receiver = p_dut,
1076            receiver_aware_session_cb_handler= dut1_y_pid,
1077            discovery_session = dut2_y_sid.callback_id,
1078            peer=dut2_peer_id_for_dut1_y,
1079            send_message =y_msg,
1080            send_message_id = _MSG_ID_SUB_TO_PUB,
1081            )
1082        logging.info(
1083            'The DUT2.Y sent a message and the DUT1 received it.'
1084            )
1085
1086    def run_multiple_concurrent_services_same_name_diff_ssi(self, type_x, type_y):
1087        """Validate same service name with multiple service specific info on publisher
1088        and subscriber can see all service
1089
1090        - p_dut running Publish X and Y
1091        - s_dut running subscribe A and B
1092        - subscribe A find X and Y
1093        - subscribe B find X and Y
1094
1095        Message exchanges:
1096            - A to X and X to A
1097            - B to X and X to B
1098            - A to Y and Y to A
1099         - B to Y and Y to B
1100
1101        Note: test requires that publisher device support 2 publish sessions concurrently,
1102        and subscriber device support 2 subscribe sessions concurrently.
1103        The test will be skipped if the devices are not capable.
1104
1105        Args:
1106            type_x, type_y: A list of [ptype, stype] of the publish and subscribe
1107                      types for services X and Y respectively.
1108         """
1109        p_dut = self.ads[0]
1110        s_dut = self.ads[1]
1111        asserts.skip_if(
1112            autils.get_aware_capabilities(p_dut)["maxPublishes"] < 2
1113            or autils.get_aware_capabilities(s_dut)["maxPublishes"] < 2
1114            ,"Devices do not support 2 publish sessions"
1115            )
1116        SERVICE_NAME = "ServiceName"
1117        X_SERVICE_SSI = "ServiceSpecificInfoXXX"
1118        Y_SERVICE_SSI = "ServiceSpecificInfoYYY"
1119        # use_id = True
1120        # attach and wait for confirmation
1121        p_id = self._start_attach(p_dut)
1122        s_id = self._start_attach(s_dut)
1123        p_disc_id_x = p_dut.wifi_aware_snippet.wifiAwarePublish(
1124            p_id, autils.create_discovery_config(SERVICE_NAME, type_x[0], None, X_SERVICE_SSI)
1125                )
1126        p_dut.log.info('Created the DUT1 X publish session.')
1127        p_discovery = p_disc_id_x.waitAndGet(
1128            constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT)
1129        callback_name = p_discovery.data[_CALLBACK_NAME]
1130        asserts.assert_equal(
1131            constants.DiscoverySessionCallbackMethodType.PUBLISH_STARTED,
1132            callback_name,
1133            f'{p_dut} DUT1 X publish failed, got callback: {callback_name}.',
1134            )
1135        p_disc_id_y = p_dut.wifi_aware_snippet.wifiAwarePublish(
1136                p_id, autils.create_discovery_config(SERVICE_NAME, type_x[0], None, Y_SERVICE_SSI)
1137                )
1138        p_dut.log.info('Created the DUT1 Y publish session.')
1139        p_discovery = p_disc_id_y.waitAndGet(
1140            constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT)
1141        callback_name = p_discovery.data[_CALLBACK_NAME]
1142        asserts.assert_equal(
1143            constants.DiscoverySessionCallbackMethodType.PUBLISH_STARTED,
1144            callback_name,
1145            f'{p_dut} DUT1 Y publish failed, got callback: {callback_name}.',
1146            )
1147        # Subscriber: start subscribe session A
1148        s_disc_id_a = s_dut.wifi_aware_snippet.wifiAwareSubscribe(
1149            s_id, autils.create_discovery_config(SERVICE_NAME, None, type_x[1] )
1150            )
1151        s_dut.log.info('Created the DUT2 X subscribe session.')
1152        s_discovery = s_disc_id_a.waitAndGet(
1153                constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT,
1154                timeout=_DEFAULT_TIMEOUT)
1155        callback_name = s_discovery.data[_CALLBACK_NAME]
1156        asserts.assert_equal(
1157            constants.DiscoverySessionCallbackMethodType.SUBSCRIBE_STARTED,
1158            callback_name,
1159            f'{s_dut} DUT2 X subscribe failed, got callback: {callback_name}.',
1160            )
1161        # Subscriber: start subscribe session B
1162        s_disc_id_b = s_dut.wifi_aware_snippet.wifiAwareSubscribe(
1163                s_id, autils.create_discovery_config(SERVICE_NAME, None, type_y[1])
1164                )
1165        s_dut.log.info('Created the DUT2 Y subscribe session.')
1166        s_discovery = s_disc_id_b.waitAndGet(
1167            constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT)
1168        callback_name = s_discovery.data[_CALLBACK_NAME]
1169        asserts.assert_equal(
1170            constants.DiscoverySessionCallbackMethodType.SUBSCRIBE_STARTED,
1171            callback_name,
1172            f'{s_dut} DUT2 Y subscribe failed, got callback: {callback_name}.',
1173            )
1174        session_name = {"pub": {p_disc_id_x: "X", p_disc_id_y: "Y"},
1175                        "sub": {s_disc_id_a: "A", s_disc_id_b: "B"}}
1176        # Subscriber: subscribe session A & B wait for service discovery
1177        # Number of results on each session should be exactly 2
1178        results_a = {}
1179        for i in range(2):
1180            event = s_disc_id_a.waitAndGet(
1181                constants.DiscoverySessionCallbackMethodType.SERVICE_DISCOVERED)
1182            results_a[
1183                bytes(event.data[constants.WifiAwareSnippetParams.SERVICE_SPECIFIC_INFO]).decode("utf-8")] = event
1184        autils.callback_no_response(
1185            s_disc_id_a, constants.DiscoverySessionCallbackMethodType.SERVICE_DISCOVERED, 10, True
1186        )
1187        results_b = {}
1188        for i in range(2):
1189            event = s_disc_id_b.waitAndGet(
1190                constants.DiscoverySessionCallbackMethodType.SERVICE_DISCOVERED)
1191            results_b[
1192                bytes(event.data[constants.WifiAwareSnippetParams.SERVICE_SPECIFIC_INFO]).decode("utf-8")] = event
1193        autils.callback_no_response(
1194            s_disc_id_b, constants.DiscoverySessionCallbackMethodType.SERVICE_DISCOVERED, 10, True
1195        )
1196        s_a_peer_id_for_p_x = results_a[X_SERVICE_SSI].data[constants.WifiAwareSnippetParams.PEER_ID]
1197        s_a_peer_id_for_p_y = results_a[Y_SERVICE_SSI].data[constants.WifiAwareSnippetParams.PEER_ID]
1198        s_b_peer_id_for_p_x = results_b[X_SERVICE_SSI].data[constants.WifiAwareSnippetParams.PEER_ID]
1199        s_b_peer_id_for_p_y = results_b[Y_SERVICE_SSI].data[constants.WifiAwareSnippetParams.PEER_ID]
1200
1201        # Message exchange between Publisher and Subscribe
1202        self.exchange_messages(p_dut, p_disc_id_x,
1203                               s_dut, s_disc_id_a, s_a_peer_id_for_p_x, session_name)
1204
1205        self.exchange_messages(p_dut, p_disc_id_x,
1206                               s_dut, s_disc_id_b, s_b_peer_id_for_p_x, session_name)
1207
1208        self.exchange_messages(p_dut, p_disc_id_y,
1209                               s_dut, s_disc_id_a, s_a_peer_id_for_p_y, session_name)
1210
1211        self.exchange_messages(p_dut, p_disc_id_y,
1212                               s_dut, s_disc_id_b, s_b_peer_id_for_p_y, session_name)
1213
1214    def run_service_discovery_on_service_lost(self, p_type, s_type):
1215        """
1216        Validate service lost callback will be receive on subscriber, when publisher stopped publish
1217        - p_dut running Publish
1218        - s_dut running subscribe
1219        - s_dut discover p_dut
1220        - p_dut stop publish
1221        - s_dut receive service lost callback
1222
1223        Args:
1224            p_type: Publish discovery type
1225            s_type: Subscribe discovery type
1226        """
1227        p_dut = self.ads[0]
1228        s_dut = self.ads[1]
1229        # attach and wait for confirmation
1230        p_id = self._start_attach(p_dut)
1231        s_id = self._start_attach(s_dut)
1232        p_config = self.create_publish_config(
1233            p_dut.wifi_aware_snippet.getCharacteristics(),
1234            p_type,
1235            _PAYLOAD_SIZE_TYPICAL,
1236            ttl=0,
1237            term_ind_on=False,
1238            null_match=False,
1239            )
1240        p_disc_id = p_dut.wifi_aware_snippet.wifiAwarePublish(
1241            p_id, p_config
1242            )
1243        p_dut.log.info('Created the publish session.')
1244        p_discovery = p_disc_id.waitAndGet(
1245            constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT)
1246        callback_name = p_discovery.data[_CALLBACK_NAME]
1247        asserts.assert_equal(
1248            constants.DiscoverySessionCallbackMethodType.PUBLISH_STARTED,
1249            callback_name,
1250            f'{p_dut} publish failed, got callback: {callback_name}.',
1251            )
1252        s_config = self.create_subscribe_config(
1253            s_dut.wifi_aware_snippet.getCharacteristics(),
1254            s_type,
1255            _PAYLOAD_SIZE_TYPICAL,
1256            ttl=0,
1257            term_ind_on=False,
1258            null_match=True,
1259            )
1260        s_disc_id = s_dut.wifi_aware_snippet.wifiAwareSubscribe(
1261            s_id, s_config
1262            )
1263        s_dut.log.info('Created the subscribe session.')
1264        s_discovery = s_disc_id.waitAndGet(
1265            constants.DiscoverySessionCallbackMethodType.DISCOVER_RESULT)
1266        callback_name = s_discovery.data[_CALLBACK_NAME]
1267        asserts.assert_equal(
1268            constants.DiscoverySessionCallbackMethodType.SUBSCRIBE_STARTED,
1269            callback_name,
1270            f'{s_dut} subscribe failed, got callback: {callback_name}.'
1271        )
1272        discovered_event = s_disc_id.waitAndGet(
1273            constants.DiscoverySessionCallbackMethodType.SERVICE_DISCOVERED)
1274        peer_id_on_sub = discovered_event.data[
1275            constants.WifiAwareSnippetParams.PEER_ID]
1276        # Publisher+Subscriber: Terminate sessions
1277        p_dut.wifi_aware_snippet.wifiAwareCloseDiscoverSession(
1278            p_disc_id.callback_id)
1279        time.sleep(10)
1280        # service_lost_event = s_disc_id.waitAndGet("WifiAwareSessionOnServiceLost")
1281        service_lost_event = s_disc_id.waitAndGet(
1282            constants.DiscoverySessionCallbackMethodType.SESSION_CB_ON_SERVICE_LOST)
1283        asserts.assert_equal(peer_id_on_sub,
1284                             service_lost_event.data[constants.WifiAwareSnippetParams.PEER_ID])
1285        asserts.assert_equal(
1286            constants.EASON_PEER_NOT_VISIBLE,
1287            service_lost_event.data[constants.DiscoverySessionCallbackMethodType.SESSION_CB_KEY_LOST_REASON]
1288            )
1289
1290    def test_positive_unsolicited_passive_typical(self)-> None:
1291        """Functional test case / Discovery test cases / positive test case:
1292        - Unsolicited publish + passive subscribe
1293        - Typical payload fields size
1294
1295        Verifies that discovery and message exchange succeeds.
1296        """
1297        self._positive_discovery_logic(
1298             _PUBLISH_TYPE_UNSOLICITED,
1299             _SUBSCRIBE_TYPE_PASSIVE,
1300             _PAYLOAD_SIZE_TYPICAL
1301            )
1302
1303    def test_positive_unsolicited_passive_min(self)-> None:
1304        """Functional test case / Discovery test cases / positive test case:
1305        - Unsolicited publish + passive subscribe
1306        - Minimal payload fields size
1307
1308        Verifies that discovery and message exchange succeeds.
1309        """
1310        self._positive_discovery_logic(
1311             _PUBLISH_TYPE_UNSOLICITED,
1312             _SUBSCRIBE_TYPE_PASSIVE,
1313             _PAYLOAD_SIZE_MIN
1314            )
1315
1316    def test_positive_unsolicited_passive_max(self)-> None:
1317        """Functional test case / Discovery test cases / positive test case:
1318        - Unsolicited publish + passive subscribe
1319        - Maximal payload fields size
1320
1321        Verifies that discovery and message exchange succeeds.
1322        """
1323        self._positive_discovery_logic(
1324             _PUBLISH_TYPE_UNSOLICITED,
1325             _SUBSCRIBE_TYPE_PASSIVE,
1326             _PAYLOAD_SIZE_MAX
1327            )
1328
1329    def test_positive_solicited_active_typical(self)-> None:
1330        """Functional test case / Discovery test cases / positive test case:
1331        - Solicited publish + active subscribe
1332        - Typical payload fields size
1333
1334        Verifies that discovery and message exchange succeeds.
1335        """
1336        self._positive_discovery_logic(
1337             _PUBLISH_TYPE_SOLICITED,
1338             _SUBSCRIBE_TYPE_ACTIVE,
1339             _PAYLOAD_SIZE_TYPICAL
1340            )
1341
1342    def test_positive_solicited_active_min(self)-> None:
1343        """Functional test case / Discovery test cases / positive test case:
1344        - Solicited publish + active subscribe
1345        - Minimal payload fields size
1346
1347        Verifies that discovery and message exchange succeeds.
1348        """
1349        self._positive_discovery_logic(
1350             _PUBLISH_TYPE_SOLICITED,
1351             _SUBSCRIBE_TYPE_ACTIVE,
1352             _PAYLOAD_SIZE_MIN
1353            )
1354
1355    def test_positive_solicited_active_max(self)-> None:
1356        """Functional test case / Discovery test cases / positive test case:
1357        - Solicited publish + active subscribe
1358        - Maximal payload fields size
1359
1360        Verifies that discovery and message exchange succeeds.
1361        """
1362        self._positive_discovery_logic(
1363             _PUBLISH_TYPE_SOLICITED,
1364             _SUBSCRIBE_TYPE_ACTIVE,
1365             _PAYLOAD_SIZE_MAX
1366            )
1367
1368    #######################################
1369    # TTL tests key:
1370    #
1371    # names is: test_ttl_<pub_type|sub_type>_<term_ind>
1372    # where:
1373    #
1374    # pub_type: Type of publish discovery session: unsolicited or solicited.
1375    # sub_type: Type of subscribe discovery session: passive or active.
1376    # term_ind: ind_on or ind_off
1377    #######################################
1378
1379    def test_ttl_unsolicited_ind_on(self)-> None:
1380        """Functional test case / Discovery test cases / TTL test case:
1381        - Unsolicited publish
1382        - Termination indication enabled
1383        """
1384        self.positive_ttl_test_utility(
1385            is_publish=True,
1386            ptype=_PUBLISH_TYPE_UNSOLICITED,
1387            stype=None,
1388            term_ind_on=True)
1389
1390    def test_ttl_unsolicited_ind_off(self)-> None:
1391        """Functional test case / Discovery test cases / TTL test case:
1392        - Unsolicited publish
1393        - Termination indication disabled
1394        """
1395        self.positive_ttl_test_utility(
1396            is_publish=True,
1397            ptype=_PUBLISH_TYPE_UNSOLICITED,
1398            stype=None,
1399            term_ind_on=False)
1400
1401    def test_ttl_solicited_ind_on(self)-> None:
1402        """Functional test case / Discovery test cases / TTL test case:
1403        - Solicited publish
1404        - Termination indication enabled
1405        """
1406        self.positive_ttl_test_utility(
1407            is_publish=True,
1408            ptype=_PUBLISH_TYPE_SOLICITED,
1409            stype=None,
1410            term_ind_on=True)
1411
1412    def test_ttl_solicited_ind_off(self)-> None:
1413        """Functional test case / Discovery test cases / TTL test case:
1414        - Solicited publish
1415        - Termination indication disabled
1416        """
1417        self.positive_ttl_test_utility(
1418            is_publish=True,
1419            ptype=_PUBLISH_TYPE_SOLICITED,
1420            stype=None,
1421            term_ind_on=False)
1422
1423    def test_ttl_passive_ind_on(self)-> None:
1424        """Functional test case / Discovery test cases / TTL test case:
1425        - Passive subscribe
1426        - Termination indication enabled
1427        """
1428        self.positive_ttl_test_utility(
1429            is_publish=False,
1430            ptype=None,
1431            stype=_SUBSCRIBE_TYPE_PASSIVE,
1432            term_ind_on=True)
1433
1434    def test_ttl_passive_ind_off(self)-> None:
1435        """Functional test case / Discovery test cases / TTL test case:
1436        - Passive subscribe
1437        - Termination indication disabled
1438        """
1439        self.positive_ttl_test_utility(
1440            is_publish=False,
1441            ptype=None,
1442            stype=_SUBSCRIBE_TYPE_PASSIVE,
1443            term_ind_on=False)
1444
1445    def test_ttl_active_ind_on(self)-> None:
1446        """Functional test case / Discovery test cases / TTL test case:
1447        - Active subscribe
1448        - Termination indication enabled
1449        """
1450        self.positive_ttl_test_utility(
1451            is_publish=False,
1452            ptype=None,
1453            stype=_SUBSCRIBE_TYPE_ACTIVE,
1454            term_ind_on=True)
1455
1456    def test_ttl_active_ind_off(self)-> None:
1457        """Functional test case / Discovery test cases / TTL test case:
1458        - Active subscribe
1459        - Termination indication disabled
1460        """
1461        self.positive_ttl_test_utility(
1462            is_publish=False,
1463            ptype=None,
1464            stype=_SUBSCRIBE_TYPE_ACTIVE,
1465            term_ind_on=False)
1466
1467    #######################################
1468    # Mismatched discovery session type tests key:
1469    #
1470    # names is: test_mismatch_service_type_<pub_type>_<sub_type>
1471    # where:
1472    #
1473    # pub_type: Type of publish discovery session: unsolicited or solicited.
1474    # sub_type: Type of subscribe discovery session: passive or active.
1475    #######################################
1476
1477    def test_mismatch_service_type_unsolicited_active(self):
1478        """Functional test case / Discovery test cases / Mismatch service name
1479    - Unsolicited publish
1480    - Active subscribe
1481    """
1482        self.discovery_mismatch_test_utility(
1483            is_expected_to_pass=True,
1484            p_type=_PUBLISH_TYPE_UNSOLICITED,
1485            s_type=_SUBSCRIBE_TYPE_ACTIVE)
1486
1487    def test_mismatch_service_type_solicited_passive(self):
1488        """Functional test case / Discovery test cases / Mismatch service name
1489    - Unsolicited publish
1490    - Active subscribe
1491    """
1492        self.discovery_mismatch_test_utility(
1493            is_expected_to_pass=False,
1494            p_type = _PUBLISH_TYPE_SOLICITED,
1495            s_type = _SUBSCRIBE_TYPE_PASSIVE)
1496
1497    ######################################
1498    # Mismatched service name tests key:
1499    #
1500    # names is: test_mismatch_service_name_<pub_type>_<sub_type>
1501    # where:
1502    #
1503    # pub_type: Type of publish discovery session: unsolicited or solicited.
1504    # sub_type: Type of subscribe discovery session: passive or active.
1505    #######################################
1506
1507    def test_mismatch_service_name_unsolicited_passive(self):
1508        """Functional test case / Discovery test cases / Mismatch service name
1509    - Unsolicited publish
1510    - Passive subscribe
1511    """
1512        self.discovery_mismatch_test_utility(
1513            is_expected_to_pass=False,
1514            p_type=_PUBLISH_TYPE_UNSOLICITED,
1515            s_type=_SUBSCRIBE_TYPE_PASSIVE,
1516            p_service_name="GoogleTestServiceXXX",
1517            s_service_name="GoogleTestServiceYYY")
1518
1519    def test_mismatch_service_name_solicited_active(self):
1520        """Functional test case / Discovery test cases / Mismatch service name
1521    - Solicited publish
1522    - Active subscribe
1523    """
1524        self.discovery_mismatch_test_utility(
1525            is_expected_to_pass=False,
1526            p_type=_PUBLISH_TYPE_SOLICITED,
1527            s_type=_SUBSCRIBE_TYPE_ACTIVE,
1528            p_service_name="GoogleTestServiceXXX",
1529            s_service_name="GoogleTestServiceYYY")
1530
1531    #######################################
1532    # Mismatched discovery match filter tests key:
1533    #
1534    # names is: test_mismatch_match_filter_<pub_type>_<sub_type>
1535    # where:
1536    #
1537    # pub_type: Type of publish discovery session: unsolicited or solicited.
1538    # sub_type: Type of subscribe discovery session: passive or active.
1539    #######################################
1540
1541    def test_mismatch_match_filter_unsolicited_passive(self):
1542        """Functional test case / Discovery test cases / Mismatch match filter
1543    - Unsolicited publish
1544    - Passive subscribe
1545    """
1546        self.discovery_mismatch_test_utility(
1547            is_expected_to_pass=False,
1548            p_type=_PUBLISH_TYPE_UNSOLICITED,
1549            s_type=_SUBSCRIBE_TYPE_PASSIVE,
1550            p_mf_1="hello there string",
1551            s_mf_1="goodbye there string")
1552
1553    def test_mismatch_match_filter_solicited_active(self):
1554        """Functional test case / Discovery test cases / Mismatch match filter
1555    - Solicited publish
1556    - Active subscribe
1557    """
1558        self.discovery_mismatch_test_utility(
1559            is_expected_to_pass=False,
1560            p_type=_PUBLISH_TYPE_SOLICITED,
1561            s_type=_SUBSCRIBE_TYPE_ACTIVE,
1562            p_mf_1="hello there string",
1563            s_mf_1="goodbye there string")
1564
1565    #########################################################
1566    # Multiple concurrent services
1567    #######################################
1568
1569    def test_multiple_concurrent_services_both_unsolicited_passive(self):
1570        """Validate multiple concurrent discovery sessions running on both devices.
1571    - DUT1 & DUT2 running Publish for X
1572    - DUT1 & DUT2 running Publish for Y
1573    - DUT1 Subscribes for X
1574    - DUT2 Subscribes for Y
1575    Message exchanges.
1576
1577    Both sessions are Unsolicited/Passive.
1578
1579    Note: test requires that devices support 2 publish sessions concurrently.
1580    The test will be skipped if the devices are not capable.
1581    """
1582        self.run_multiple_concurrent_services(
1583            type_x=[
1584                _PUBLISH_TYPE_UNSOLICITED,
1585                _SUBSCRIBE_TYPE_PASSIVE
1586            ],
1587            type_y=[
1588                _PUBLISH_TYPE_UNSOLICITED,
1589                _SUBSCRIBE_TYPE_PASSIVE
1590            ])
1591
1592    def test_multiple_concurrent_services_both_solicited_active(self):
1593        """Validate multiple concurrent discovery sessions running on both devices.
1594    - DUT1 & DUT2 running Publish for X
1595    - DUT1 & DUT2 running Publish for Y
1596    - DUT1 Subscribes for X
1597    - DUT2 Subscribes for Y
1598    Message exchanges.
1599
1600    Both sessions are Solicited/Active.
1601
1602    Note: test requires that devices support 2 publish sessions concurrently.
1603    The test will be skipped if the devices are not capable.
1604    """
1605        self.run_multiple_concurrent_services(
1606            type_x=[
1607                _PUBLISH_TYPE_SOLICITED,
1608                _SUBSCRIBE_TYPE_ACTIVE
1609            ],
1610            type_y=[
1611                _PUBLISH_TYPE_SOLICITED, _SUBSCRIBE_TYPE_ACTIVE
1612            ])
1613
1614    def test_multiple_concurrent_services_mix_unsolicited_solicited(self):
1615        """Validate multiple concurrent discovery sessions running on both devices.
1616    - DUT1 & DUT2 running Publish for X
1617    - DUT1 & DUT2 running Publish for Y
1618    - DUT1 Subscribes for X
1619    - DUT2 Subscribes for Y
1620    Message exchanges.
1621
1622    Session A is Unsolicited/Passive.
1623    Session B is Solicited/Active.
1624
1625    Note: test requires that devices support 2 publish sessions concurrently.
1626    The test will be skipped if the devices are not capable.
1627    """
1628        self.run_multiple_concurrent_services(
1629            type_x=[
1630                _PUBLISH_TYPE_UNSOLICITED,
1631                _SUBSCRIBE_TYPE_PASSIVE
1632            ],
1633            type_y=[
1634                _PUBLISH_TYPE_SOLICITED, _SUBSCRIBE_TYPE_ACTIVE
1635            ])
1636
1637    #########################################################
1638    # Multiple concurrent services with diff ssi
1639    #########################################################
1640
1641    def test_multiple_concurrent_services_diff_ssi_unsolicited_passive(self):
1642        """Multi service test on same service name but different Service Specific Info
1643        - Unsolicited publish
1644        - Passive subscribe
1645        """
1646        self.run_multiple_concurrent_services_same_name_diff_ssi(
1647            type_x=[_PUBLISH_TYPE_UNSOLICITED, _SUBSCRIBE_TYPE_PASSIVE],
1648            type_y=[_PUBLISH_TYPE_UNSOLICITED, _SUBSCRIBE_TYPE_PASSIVE])
1649
1650    def test_multiple_concurrent_services_diff_ssi_solicited_active(self):
1651        """Multi service test on same service name but different Service Specific Info
1652        - Solicited publish
1653        - Active subscribe
1654        """
1655        self.run_multiple_concurrent_services_same_name_diff_ssi(
1656            type_x=[_PUBLISH_TYPE_SOLICITED, _SUBSCRIBE_TYPE_ACTIVE],
1657            type_y=[_PUBLISH_TYPE_SOLICITED, _SUBSCRIBE_TYPE_ACTIVE])
1658
1659    #########################################################
1660
1661    def test_upper_lower_service_name_equivalence(self):
1662        """Validate that Service Name is case-insensitive. Publish a service name
1663        with mixed case, subscribe to the same service name with alternative case
1664        and verify that discovery happens."""
1665        p_dut = self.ads[0]
1666        s_dut = self.ads[1]
1667
1668        pub_service_name = "GoogleAbCdEf"
1669        sub_service_name = "GoogleaBcDeF"
1670        p_config = autils.create_discovery_config(pub_service_name)
1671        p_config[constants.PUBLISH_TYPE] = _PUBLISH_TYPE_UNSOLICITED
1672        s_config = autils.create_discovery_config(sub_service_name)
1673        s_config[constants.SUBSCRIBE_TYPE] = _SUBSCRIBE_TYPE_PASSIVE
1674        self.create_discovery_pair(
1675            p_dut,
1676            s_dut,
1677            p_config,
1678            s_config)
1679
1680    #########################################################
1681    # service discovery on service lost
1682    #########################################################
1683
1684    def test_service_discovery_on_service_lost_unsolicited_passive(self):
1685        """
1686        Test service discovery lost with unsolicited publish and passive subscribe
1687        """
1688        self.run_service_discovery_on_service_lost(_PUBLISH_TYPE_UNSOLICITED,
1689                                                   _SUBSCRIBE_TYPE_PASSIVE)
1690
1691    def test_service_discovery_on_service_lost_solicited_active(self):
1692        """
1693        Test service discovery lost with solicited publish and active subscribe
1694        """
1695        self.run_service_discovery_on_service_lost(_PUBLISH_TYPE_SOLICITED,
1696                                                   _SUBSCRIBE_TYPE_ACTIVE)
1697
1698if __name__ == '__main__':
1699    # Take test args
1700    if '--' in sys.argv:
1701        index = sys.argv.index('--')
1702        sys.argv = sys.argv[:1] + sys.argv[index + 1:]
1703
1704    test_runner.main()
1705