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