• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2023 Google LLC
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#     https://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
15import asyncio
16import hci_packets as hci
17import link_layer_packets as ll
18import math
19import random
20import unittest
21from dataclasses import dataclass
22from hci_packets import ErrorCode, FragmentPreference
23from py.bluetooth import Address
24from py.controller import ControllerTest
25from typing import List
26
27
28def make_advertising_event_properties(properties: int) -> hci.AdvertisingEventProperties:
29    return hci.AdvertisingEventProperties(connectable=(properties & 0x1) != 0,
30                                          scannable=(properties & 0x2) != 0,
31                                          directed=(properties & 0x4) != 0,
32                                          high_duty_cycle=(properties & 0x8) != 0,
33                                          legacy=(properties & 0x10) != 0,
34                                          anonymous=(properties & 0x20) != 0,
35                                          include_tx_power=(properties & 0x40) != 0)
36
37
38@dataclass
39class TestRound:
40    advertising_event_properties: int
41    data_length: int
42    fragment_preference: FragmentPreference
43    duration: int
44    max_extended_advertising_events: int
45
46
47class Test(ControllerTest):
48    # Test parameters.
49    LL_advertiser_advInterval_MIN = 0x200
50    LL_advertiser_advInterval_MAX = 0x200
51    LL_advertiser_Adv_Channel_Map = 0x7
52    LL_initiator_connInterval = 0x200
53    LL_initiator_connPeripheralLatency = 0x200
54    LL_initiator_connSupervisionTimeout = 0x200
55
56    # LL/DDI/ADV/BV-47-C [Extended Advertising, Non-Connectable – LE 1M PHY]
57    async def test(self):
58        controller = self.controller
59
60        # 1. The Upper Tester sends an HCI_LE_Read_Maximum_Advertising_Data_Length command to the
61        # IUT and expects the IUT to return a Maximum_Advertising_Data_Length between 0x001F and
62        # 0x0672. The Upper Tester stores the Maximum_Advertising_Data_Length for future use.
63        controller.send_cmd(hci.LeReadMaximumAdvertisingDataLength())
64
65        event = await self.expect_cmd_complete(hci.LeReadMaximumAdvertisingDataLengthComplete)
66        maximum_advertising_data_length = event.maximum_advertising_data_length
67
68        # Test rounds.
69        test_rounds = [
70            TestRound(0x0, 0, FragmentPreference.CONTROLLER_MAY_FRAGMENT, 0x0, 0x0),
71            TestRound(0x0, 31, FragmentPreference.CONTROLLER_MAY_FRAGMENT, 0x0, 0x0),
72            TestRound(0x0, 474, FragmentPreference.CONTROLLER_MAY_FRAGMENT, 0x0, 0x0),
73            TestRound(0x0, 711, FragmentPreference.CONTROLLER_MAY_FRAGMENT, 0x0, 0x0),
74            TestRound(0x0, 948, FragmentPreference.CONTROLLER_MAY_FRAGMENT, 0x0, 0x0),
75            TestRound(0x0, maximum_advertising_data_length, FragmentPreference.CONTROLLER_MAY_FRAGMENT, 0x0, 0x0),
76            TestRound(0x0, maximum_advertising_data_length, FragmentPreference.CONTROLLER_SHOULD_NOT, 0x0, 0x0),
77            TestRound(0x4, 0, FragmentPreference.CONTROLLER_MAY_FRAGMENT, 0x0, 0x0),
78            TestRound(0x4, 251, FragmentPreference.CONTROLLER_MAY_FRAGMENT, 0x0, 0x0),
79            TestRound(0x4, maximum_advertising_data_length, FragmentPreference.CONTROLLER_MAY_FRAGMENT, 0x0, 0x0),
80            TestRound(0x0, 0, FragmentPreference.CONTROLLER_MAY_FRAGMENT, 0x1f4, 0x0),
81            TestRound(0x4, 0, FragmentPreference.CONTROLLER_MAY_FRAGMENT, 0x1f4, 0x0),
82            TestRound(0x0, 0, FragmentPreference.CONTROLLER_MAY_FRAGMENT, 0x0, 0x32),
83            TestRound(0x4, 0, FragmentPreference.CONTROLLER_MAY_FRAGMENT, 0x0, 0x32),
84        ]
85
86        # 14. Repeat steps 2–13 for each Round shown in Table 4.6
87        for test_round in test_rounds:
88            await self.steps_2_13(maximum_advertising_data_length, **vars(test_round))
89
90    async def steps_2_13(self, maximum_advertising_data_length: int, advertising_event_properties: int,
91                         data_length: int, fragment_preference: FragmentPreference, duration: int,
92                         max_extended_advertising_events: int):
93        controller = self.controller
94        advertising_event_properties = make_advertising_event_properties(advertising_event_properties)
95
96        # 2. If the Data Length listed in Table 4.6 for the current Round is less than or equal to the
97        # Maximum_Advertising_Data_Length proceed to step 3, otherwise skip to step 14.
98        if data_length > maximum_advertising_data_length:
99            return
100
101        # 3. The Upper Tester sends an HCI_LE_Set_Extended_Advertising_Parameters command to the
102        # IUT using all supported advertising channels and a selected advertising interval between the
103        # minimum and maximum advertising intervals supported. Advertising_Event_Properties parameter
104        # shall be set to the value specified in Table 4.6 for this round. The Primary_Advertising_PHY and
105        # Secondary_Advertising_PHY shall be set to the values specified in Table 4.5. If the
106        # Advertising_Event_Properties value for this Round specifies directed advertising, the
107        # Peer_Address_Type shall be set to 0x00 (Public Device Address), and the Peer_Address shall be
108        # set to the Lower Tester’s address.
109        controller.send_cmd(
110            hci.LeSetExtendedAdvertisingParametersV1(
111                advertising_handle=0,
112                advertising_event_properties=advertising_event_properties,
113                primary_advertising_interval_min=self.LL_advertiser_advInterval_MIN,
114                primary_advertising_interval_max=self.LL_advertiser_advInterval_MAX,
115                primary_advertising_channel_map=self.LL_advertiser_Adv_Channel_Map,
116                own_address_type=hci.OwnAddressType.PUBLIC_DEVICE_ADDRESS,
117                advertising_filter_policy=hci.AdvertisingFilterPolicy.ALL_DEVICES,
118                primary_advertising_phy=hci.PrimaryPhyType.LE_1M))
119
120        await self.expect_evt(
121            hci.LeSetExtendedAdvertisingParametersV1Complete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
122
123        # 4. The Upper Tester sends one or more HCI_LE_Set_Extended_Advertising_Data commands to the
124        # IUT with values according to Table 4.6 and using random octets from 1 to 254 as the payload. If
125        # the Data Length is greater than 251 the Upper Tester shall send multiple commands using one
126        # Operation 0x01 (First fragment) command, followed by zero or more Operation 0x00
127        # (Intermediate Fragment) commands, and a final Operation 0x02 (Last fragment) command.
128        # Otherwise the Upper Tester shall send a single command using Operation 0x03 (Complete Data).
129        advertising_data = [random.randint(1, 254) for n in range(data_length)]
130        num_fragments = math.ceil(data_length / 251) or 1  # Make sure to set the advertising data if it is empty.
131        for n in range(num_fragments):
132            fragment_offset = 251 * n
133            fragment_length = min(251, data_length - fragment_offset)
134            if num_fragments == 1:
135                operation = hci.Operation.COMPLETE_ADVERTISEMENT
136            elif n == 0:
137                operation = hci.Operation.FIRST_FRAGMENT
138            elif n == num_fragments - 1:
139                operation = hci.Operation.LAST_FRAGMENT
140            else:
141                operation = hci.Operation.INTERMEDIATE_FRAGMENT
142
143            controller.send_cmd(
144                hci.LeSetExtendedAdvertisingData(advertising_handle=0,
145                                                 operation=operation,
146                                                 advertising_data=advertising_data[fragment_offset:fragment_offset +
147                                                                                   fragment_length]))
148
149            await self.expect_evt(
150                hci.LeSetExtendedAdvertisingDataComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
151
152        # 5. The Upper Tester enables advertising using the HCI_LE_Set_Extended_Advertising_Enable
153        # command. The Duration[0] parameter shall be set to the value specified in Table 4.6 for this
154        # round. The Max_Extended_Advertising_Events[0] parameter shall be set to the value specified in
155        # Table 4.6 for this round.
156        controller.send_cmd(
157            hci.LeSetExtendedAdvertisingEnable(enable=hci.Enable.ENABLED,
158                                               enabled_sets=[
159                                                   hci.EnabledSet(
160                                                       advertising_handle=0,
161                                                       duration=duration,
162                                                       max_extended_advertising_events=max_extended_advertising_events)
163                                               ]))
164
165        await self.expect_evt(
166            hci.LeSetExtendedAdvertisingEnableComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
167
168        # 6. The Lower Tester receives an ADV_EXT_IND packet from the IUT with AdvMode set to 00b. The
169        # ADV_EXT_IND PDU shall not include the SuppInfo, SyncInfo, TxPower, ACAD, or AdvData
170        # fields. If advertising data was set in step 4, the ADV_EXT_IND PDU shall include the AuxPtr field;
171        # otherwise, the ADV_EXT_IND PDU may include the AuxPtr field. If the AuxPtr field is included,
172        # the ADV_EXT_IND PDU shall also include the ADI field with the SID set to the value used in step
173        # 3; otherwise that field shall not be included.
174
175        # 7. If the AuxPtr is absent, skip to step 10.
176
177        # 8. The Lower Tester utilizes the AuxPtr field to listen for an AUX_ADV_IND PDU on the secondary
178        # advertising channel with the AdvMode field set to 00b. The AUX_ADV_IND PDU shall not include
179        # the SuppInfo, SyncInfo, or TxPower fields. The AUX_ADV_IND PDU shall include the ADI field
180        # matching the ADI field from step 6. If the AUX_ADV_IND PDU does not contain all the data
181        # submitted in step 4 (if any), it shall include an AuxPtr field.
182
183        # 9. If the AUX_ADV_IND PDU contains an AuxPtr field, the Lower Tester utilizes it to listen for an
184        # AUX_CHAIN_IND PDU with the AdvMode field set to 00b. The AUX_CHAIN_IND PDU shall
185        # include the ADI field matching the ADI field from step 6 and the AdvData field containing
186        # additional data submitted in step 4. The AUX_CHAIN_IND PDU shall not include the AdvA,
187        # TargetA, SuppInfo, TxPower, or SyncInfo fields. If the AUX_CHAIN_IND PDU contains an AuxPtr
188        # field this step is repeated until an AUX_CHAIN_IND PDU is received with no AuxPtr field and all
189        # data has been received.
190        repeat = max_extended_advertising_events or 3
191        for n in range(repeat):
192            await self.expect_ll(
193                ll.LeExtendedAdvertisingPdu(source_address=controller.address,
194                                            advertising_address_type=ll.AddressType.PUBLIC,
195                                            target_address_type=ll.AddressType.PUBLIC,
196                                            connectable=int(advertising_event_properties.connectable),
197                                            scannable=int(advertising_event_properties.scannable),
198                                            directed=int(advertising_event_properties.directed),
199                                            sid=0,
200                                            tx_power=0,
201                                            primary_phy=ll.PhyType.LE_1M,
202                                            secondary_phy=ll.PhyType.NO_PACKETS,
203                                            advertising_data=advertising_data))
204
205        # 10. If the Max_Extended_Advertising_Events was set to a value different than 0, repeat steps 6–9
206        # until the IUT stops advertising. Afterwards, the Lower Tester confirms that the IUT did not send
207        # more than Max_Extended_Advertising_Events advertising events. Upper Tester shall receive LE
208        # Advertising Set Terminated event with ErrorCode 0x43. Skip to step 13.
209        if max_extended_advertising_events > 0:
210            try:
211                # Note: The test should timeout waiting for an advertising event
212                # past Max Extended Advertising Events count.
213                await asyncio.wait_for(self.controller.receive_ll(), timeout=1)
214                self.assertTrue(False)
215            except asyncio.exceptions.TimeoutError:
216                pass
217
218            await self.expect_evt(
219                hci.LeAdvertisingSetTerminated(
220                    status=ErrorCode.ADVERTISING_TIMEOUT,
221                    advertising_handle=0,
222                    connection_handle=0,
223                    num_completed_extended_advertising_events=max_extended_advertising_events))
224
225        # 11. Otherwise if Duration was set to a value different than 0, repeat steps 6–9 until the amount of
226        # time specified for Duration has elapsed. Afterwards, the Lower Tester confirms that the IUT does
227        # not start any additional advertising events. Upper Tester shall receive LE Advertising Set
228        # Terminated event with ErrorCode 0x3C. Skip to step 13.
229        elif duration > 0:
230            try:
231                # Note: The test should timeout waiting for a directed advertising event
232                # past the direct advertising timeout.
233                end_time = asyncio.get_running_loop().time() + duration / 100
234                while asyncio.get_running_loop().time() < end_time:
235                    await asyncio.wait_for(self.controller.receive_ll(), timeout=1)
236                self.assertTrue(False)
237            except asyncio.exceptions.TimeoutError:
238                pass
239
240            await self.expect_evt(
241                hci.LeAdvertisingSetTerminated(status=ErrorCode.ADVERTISING_TIMEOUT,
242                                               advertising_handle=0,
243                                               connection_handle=0,
244                                               num_completed_extended_advertising_events=0))
245
246        # 12. Otherwise, repeat steps 6–9 until a number of advertising intervals (10) have been detected.
247
248        # 13. The Upper Tester disables advertising using the HCI_LE_Set_Extended_Advertising_Enable
249        # command.
250        controller.send_cmd(hci.LeSetExtendedAdvertisingEnable(enable=hci.Enable.DISABLED, enabled_sets=[]))
251
252        await self.expect_evt(
253            hci.LeSetExtendedAdvertisingEnableComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
254