Loading tools/rootcanal/model/controller/link_layer_controller.cc +16 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ using TaskId = rootcanal::LinkLayerController::TaskId; namespace rootcanal { constexpr milliseconds kScanRequestTimeout(200); constexpr milliseconds kNoDelayMs(0); constexpr milliseconds kPageInterval(1000); constexpr milliseconds kInquiryInterval(500); Loading Loading @@ -1293,6 +1294,7 @@ ErrorCode LinkLayerController::LeSetScanEnable(bool enable, if (!enable) { scanner_.scan_enable = false; scanner_.pending_scan_request = {}; scanner_.pending_scan_request_timeout = {}; scanner_.history.clear(); return ErrorCode::SUCCESS; } Loading Loading @@ -1322,6 +1324,7 @@ ErrorCode LinkLayerController::LeSetScanEnable(bool enable, scanner_.timeout = {}; scanner_.periodical_timeout = {}; scanner_.pending_scan_request = {}; scanner_.pending_scan_request_timeout = {}; scanner_.filter_duplicates = filter_duplicates ? bluetooth::hci::FilterDuplicates::ENABLED : bluetooth::hci::FilterDuplicates::DISABLED; Loading Loading @@ -1453,6 +1456,7 @@ ErrorCode LinkLayerController::LeSetExtendedScanEnable( if (!enable) { scanner_.scan_enable = false; scanner_.pending_scan_request = {}; scanner_.pending_scan_request_timeout = {}; scanner_.history.clear(); return ErrorCode::SUCCESS; } Loading Loading @@ -1510,6 +1514,7 @@ ErrorCode LinkLayerController::LeSetExtendedScanEnable( scanner_.timeout = {}; scanner_.periodical_timeout = {}; scanner_.pending_scan_request = {}; scanner_.pending_scan_request_timeout = {}; scanner_.filter_duplicates = filter_duplicates; scanner_.duration = duration_ms; scanner_.period = period_ms; Loading Loading @@ -3184,6 +3189,8 @@ void LinkLayerController::ScanIncomingLeLegacyAdvertisingPdu( // is connectable in the scan response report. scanner_.connectable_scan_response = connectable_advertising; scanner_.pending_scan_request = advertising_address; scanner_.pending_scan_request_timeout = std::chrono::steady_clock::now() + kScanRequestTimeout; INFO(id_, "Sending LE Scan request to advertising address {} with scanning " Loading Loading @@ -5057,6 +5064,15 @@ void LinkLayerController::LeScanning() { scanner_.timeout = now + scanner_.duration; scanner_.periodical_timeout = now + scanner_.period; } // Pending scan timeout. // Cancel the pending scan request. This may condition may be triggered // when the advertiser is stopped before sending the scan request. if (scanner_.pending_scan_request_timeout.has_value() && now >= scanner_.pending_scan_request_timeout.value()) { scanner_.pending_scan_request = {}; scanner_.pending_scan_request_timeout = {}; } } void LinkLayerController::LeSynchronization() { Loading tools/rootcanal/model/controller/link_layer_controller.h +2 −0 Original line number Diff line number Diff line Loading @@ -1066,6 +1066,8 @@ class LinkLayerController { // Save information about the advertising PDU being scanned. bool connectable_scan_response; std::optional<AddressWithType> pending_scan_request{}; std::optional<std::chrono::steady_clock::time_point> pending_scan_request_timeout{}; // Time keeping std::optional<std::chrono::steady_clock::time_point> timeout; Loading tools/rootcanal/test/LL/scan_timeout.py 0 → 100644 +111 −0 Original line number Diff line number Diff line # Copyright 2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import asyncio import hci_packets as hci import link_layer_packets as ll import unittest from hci_packets import ErrorCode from py.bluetooth import Address from py.controller import ControllerTest, generate_rpa class Test(ControllerTest): # Verify that the scanner gracefully handles missing scan responses # after a timeout. async def test(self): # Test parameters. LL_scanner_scanInterval_MIN = 0x2000 LL_scanner_scanInterval_MAX = 0x2000 LL_scanner_scanWindow_MIN = 0x200 LL_scanner_scanWindow_MAX = 0x200 LL_scanner_Adv_Channel_Map = 0x7 controller = self.controller peer_address = Address('aa:bb:cc:dd:ee:ff') controller.send_cmd( hci.LeSetScanParameters(le_scan_type=hci.LeScanType.ACTIVE, le_scan_interval=LL_scanner_scanInterval_MAX, le_scan_window=LL_scanner_scanWindow_MAX, own_address_type=hci.OwnAddressType.RESOLVABLE_OR_PUBLIC_ADDRESS, scanning_filter_policy=hci.LeScanningFilterPolicy.ACCEPT_ALL)) await self.expect_evt(hci.LeSetScanParametersComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) controller.send_cmd( hci.LeSetScanEnable(le_scan_enable=hci.Enable.ENABLED, filter_duplicates=hci.Enable.DISABLED)) await self.expect_evt(hci.LeSetScanEnableComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) controller.send_ll(ll.LeLegacyAdvertisingPdu(source_address=peer_address, advertising_address_type=ll.AddressType.RANDOM, advertising_type=ll.LegacyAdvertisingType.ADV_SCAN_IND, advertising_data=[]), rssi=-16) await self.expect_evt( hci.LeAdvertisingReport(responses=[ hci.LeAdvertisingResponse(event_type=hci.AdvertisingEventType.ADV_SCAN_IND, address_type=hci.AddressType.RANDOM_DEVICE_ADDRESS, address=peer_address, advertising_data=[], rssi=0xf0) ])) await self.expect_ll( ll.LeScan(source_address=controller.address, destination_address=peer_address, advertising_address_type=ll.AddressType.RANDOM, scanning_address_type=ll.AddressType.PUBLIC)) # No response is sent for the duration of the scan request timeout. # The next scan request must be correctly handled. await asyncio.sleep(1.0) controller.send_ll(ll.LeLegacyAdvertisingPdu(source_address=peer_address, advertising_address_type=ll.AddressType.RANDOM, advertising_type=ll.LegacyAdvertisingType.ADV_SCAN_IND, advertising_data=[]), rssi=-16) await self.expect_evt( hci.LeAdvertisingReport(responses=[ hci.LeAdvertisingResponse(event_type=hci.AdvertisingEventType.ADV_SCAN_IND, address_type=hci.AddressType.RANDOM_DEVICE_ADDRESS, address=peer_address, advertising_data=[], rssi=0xf0) ])) await self.expect_ll( ll.LeScan(source_address=controller.address, destination_address=peer_address, advertising_address_type=ll.AddressType.RANDOM, scanning_address_type=ll.AddressType.PUBLIC)) controller.send_ll(ll.LeScanResponse(source_address=peer_address, advertising_address_type=ll.AddressType.RANDOM, scan_response_data=[]), rssi=-16) await self.expect_evt( hci.LeAdvertisingReport(responses=[ hci.LeAdvertisingResponse(event_type=hci.AdvertisingEventType.SCAN_RESPONSE, address_type=hci.AddressType.RANDOM_DEVICE_ADDRESS, address=peer_address, advertising_data=[], rssi=0xf0) ])) tools/rootcanal/test/main.py +1 −0 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ tests = [ 'LMP.LIH.BV_144_C', 'LMP.LIH.BV_149_C', 'LL.scan_collision', 'LL.scan_timeout', 'LMP.page_collision', ] Loading Loading
tools/rootcanal/model/controller/link_layer_controller.cc +16 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ using TaskId = rootcanal::LinkLayerController::TaskId; namespace rootcanal { constexpr milliseconds kScanRequestTimeout(200); constexpr milliseconds kNoDelayMs(0); constexpr milliseconds kPageInterval(1000); constexpr milliseconds kInquiryInterval(500); Loading Loading @@ -1293,6 +1294,7 @@ ErrorCode LinkLayerController::LeSetScanEnable(bool enable, if (!enable) { scanner_.scan_enable = false; scanner_.pending_scan_request = {}; scanner_.pending_scan_request_timeout = {}; scanner_.history.clear(); return ErrorCode::SUCCESS; } Loading Loading @@ -1322,6 +1324,7 @@ ErrorCode LinkLayerController::LeSetScanEnable(bool enable, scanner_.timeout = {}; scanner_.periodical_timeout = {}; scanner_.pending_scan_request = {}; scanner_.pending_scan_request_timeout = {}; scanner_.filter_duplicates = filter_duplicates ? bluetooth::hci::FilterDuplicates::ENABLED : bluetooth::hci::FilterDuplicates::DISABLED; Loading Loading @@ -1453,6 +1456,7 @@ ErrorCode LinkLayerController::LeSetExtendedScanEnable( if (!enable) { scanner_.scan_enable = false; scanner_.pending_scan_request = {}; scanner_.pending_scan_request_timeout = {}; scanner_.history.clear(); return ErrorCode::SUCCESS; } Loading Loading @@ -1510,6 +1514,7 @@ ErrorCode LinkLayerController::LeSetExtendedScanEnable( scanner_.timeout = {}; scanner_.periodical_timeout = {}; scanner_.pending_scan_request = {}; scanner_.pending_scan_request_timeout = {}; scanner_.filter_duplicates = filter_duplicates; scanner_.duration = duration_ms; scanner_.period = period_ms; Loading Loading @@ -3184,6 +3189,8 @@ void LinkLayerController::ScanIncomingLeLegacyAdvertisingPdu( // is connectable in the scan response report. scanner_.connectable_scan_response = connectable_advertising; scanner_.pending_scan_request = advertising_address; scanner_.pending_scan_request_timeout = std::chrono::steady_clock::now() + kScanRequestTimeout; INFO(id_, "Sending LE Scan request to advertising address {} with scanning " Loading Loading @@ -5057,6 +5064,15 @@ void LinkLayerController::LeScanning() { scanner_.timeout = now + scanner_.duration; scanner_.periodical_timeout = now + scanner_.period; } // Pending scan timeout. // Cancel the pending scan request. This may condition may be triggered // when the advertiser is stopped before sending the scan request. if (scanner_.pending_scan_request_timeout.has_value() && now >= scanner_.pending_scan_request_timeout.value()) { scanner_.pending_scan_request = {}; scanner_.pending_scan_request_timeout = {}; } } void LinkLayerController::LeSynchronization() { Loading
tools/rootcanal/model/controller/link_layer_controller.h +2 −0 Original line number Diff line number Diff line Loading @@ -1066,6 +1066,8 @@ class LinkLayerController { // Save information about the advertising PDU being scanned. bool connectable_scan_response; std::optional<AddressWithType> pending_scan_request{}; std::optional<std::chrono::steady_clock::time_point> pending_scan_request_timeout{}; // Time keeping std::optional<std::chrono::steady_clock::time_point> timeout; Loading
tools/rootcanal/test/LL/scan_timeout.py 0 → 100644 +111 −0 Original line number Diff line number Diff line # Copyright 2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import asyncio import hci_packets as hci import link_layer_packets as ll import unittest from hci_packets import ErrorCode from py.bluetooth import Address from py.controller import ControllerTest, generate_rpa class Test(ControllerTest): # Verify that the scanner gracefully handles missing scan responses # after a timeout. async def test(self): # Test parameters. LL_scanner_scanInterval_MIN = 0x2000 LL_scanner_scanInterval_MAX = 0x2000 LL_scanner_scanWindow_MIN = 0x200 LL_scanner_scanWindow_MAX = 0x200 LL_scanner_Adv_Channel_Map = 0x7 controller = self.controller peer_address = Address('aa:bb:cc:dd:ee:ff') controller.send_cmd( hci.LeSetScanParameters(le_scan_type=hci.LeScanType.ACTIVE, le_scan_interval=LL_scanner_scanInterval_MAX, le_scan_window=LL_scanner_scanWindow_MAX, own_address_type=hci.OwnAddressType.RESOLVABLE_OR_PUBLIC_ADDRESS, scanning_filter_policy=hci.LeScanningFilterPolicy.ACCEPT_ALL)) await self.expect_evt(hci.LeSetScanParametersComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) controller.send_cmd( hci.LeSetScanEnable(le_scan_enable=hci.Enable.ENABLED, filter_duplicates=hci.Enable.DISABLED)) await self.expect_evt(hci.LeSetScanEnableComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) controller.send_ll(ll.LeLegacyAdvertisingPdu(source_address=peer_address, advertising_address_type=ll.AddressType.RANDOM, advertising_type=ll.LegacyAdvertisingType.ADV_SCAN_IND, advertising_data=[]), rssi=-16) await self.expect_evt( hci.LeAdvertisingReport(responses=[ hci.LeAdvertisingResponse(event_type=hci.AdvertisingEventType.ADV_SCAN_IND, address_type=hci.AddressType.RANDOM_DEVICE_ADDRESS, address=peer_address, advertising_data=[], rssi=0xf0) ])) await self.expect_ll( ll.LeScan(source_address=controller.address, destination_address=peer_address, advertising_address_type=ll.AddressType.RANDOM, scanning_address_type=ll.AddressType.PUBLIC)) # No response is sent for the duration of the scan request timeout. # The next scan request must be correctly handled. await asyncio.sleep(1.0) controller.send_ll(ll.LeLegacyAdvertisingPdu(source_address=peer_address, advertising_address_type=ll.AddressType.RANDOM, advertising_type=ll.LegacyAdvertisingType.ADV_SCAN_IND, advertising_data=[]), rssi=-16) await self.expect_evt( hci.LeAdvertisingReport(responses=[ hci.LeAdvertisingResponse(event_type=hci.AdvertisingEventType.ADV_SCAN_IND, address_type=hci.AddressType.RANDOM_DEVICE_ADDRESS, address=peer_address, advertising_data=[], rssi=0xf0) ])) await self.expect_ll( ll.LeScan(source_address=controller.address, destination_address=peer_address, advertising_address_type=ll.AddressType.RANDOM, scanning_address_type=ll.AddressType.PUBLIC)) controller.send_ll(ll.LeScanResponse(source_address=peer_address, advertising_address_type=ll.AddressType.RANDOM, scan_response_data=[]), rssi=-16) await self.expect_evt( hci.LeAdvertisingReport(responses=[ hci.LeAdvertisingResponse(event_type=hci.AdvertisingEventType.SCAN_RESPONSE, address_type=hci.AddressType.RANDOM_DEVICE_ADDRESS, address=peer_address, advertising_data=[], rssi=0xf0) ]))
tools/rootcanal/test/main.py +1 −0 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ tests = [ 'LMP.LIH.BV_144_C', 'LMP.LIH.BV_149_C', 'LL.scan_collision', 'LL.scan_timeout', 'LMP.page_collision', ] Loading