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