/* * Copyright 2023 The Android Open Source Project * * 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 * * http://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. */ #include "hci/le_scanning_reassembler.h" #include #include using ::testing::_; using ::testing::Eq; using namespace bluetooth; using namespace std::chrono_literals; namespace bluetooth::hci { // Event type fields. static constexpr uint16_t kScannable = 0x2; static constexpr uint16_t kScanResponse = 0x8; static constexpr uint16_t kLegacy = 0x10; static constexpr uint8_t kComplete = 0x0; static constexpr uint8_t kContinuation = 0x20; static constexpr uint8_t kTruncated = 0x40; // Defaults for other fields. static constexpr uint8_t kSidNotPresent = 0xff; // Test addresses. static const Address kTestAddress = Address({0, 1, 2, 3, 4, 5}); class LeScanningReassemblerTest : public ::testing::Test { public: LeScanningReassembler reassembler_; }; TEST_F(LeScanningReassemblerTest, trim_advertising_data) { // TrimAdvertisingData should filter out empty entries. ASSERT_EQ( LeScanningReassembler::TrimAdvertisingData({0x1, 0x2, 0x0, 0x0, 0x3, 0x4, 0x5, 0x6}), std::vector({0x1, 0x2, 0x3, 0x4, 0x5, 0x6})); // TrimAdvertisingData should remove trailing zeros. ASSERT_EQ( LeScanningReassembler::TrimAdvertisingData({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x0, 0x0}), std::vector({0x1, 0x2, 0x3, 0x4, 0x5, 0x6})); // TrimAdvertisingData should remove overflowing entries. ASSERT_EQ( LeScanningReassembler::TrimAdvertisingData({0x1, 0x2, 0x3, 0x4, 0x5}), std::vector({0x1, 0x2})); } TEST_F(LeScanningReassemblerTest, non_scannable_legacy_advertising) { // Test non scannable legacy advertising. ASSERT_EQ( reassembler_.ProcessAdvertisingReport( kLegacy | kComplete, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x1, 0x2}), std::vector({0x1, 0x2})); } TEST_F(LeScanningReassemblerTest, scannable_legacy_advertising) { // Test scannable legacy advertising with well formed advertising and // scan response payload. ASSERT_FALSE(reassembler_ .ProcessAdvertisingReport( kLegacy | kScannable | kComplete, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x1, 0x2}) .has_value()); ASSERT_EQ( reassembler_.ProcessAdvertisingReport( kLegacy | kScannable | kScanResponse | kComplete, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x3, 0x4, 0x5, 0x6}), std::vector({0x1, 0x2, 0x3, 0x4, 0x5, 0x6})); // Test scannable legacy advertising with padding after the // advertising and scan response data. ASSERT_FALSE(reassembler_ .ProcessAdvertisingReport( kLegacy | kScannable | kComplete, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x1, 0x2, 0x0, 0x0}) .has_value()); ASSERT_EQ( reassembler_.ProcessAdvertisingReport( kLegacy | kScannable | kScanResponse | kComplete, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x3, 0x4, 0x5, 0x6, 0x0, 0x0}), std::vector({0x1, 0x2, 0x3, 0x4, 0x5, 0x6})); } TEST_F(LeScanningReassemblerTest, non_scannable_extended_advertising) { // Test fragmented non scannable extended advertising. // The split may occur in the middle of a GAP entry. ASSERT_FALSE(reassembler_ .ProcessAdvertisingReport( kContinuation, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x1, 0x2, 0x3}) .has_value()); ASSERT_EQ( reassembler_.ProcessAdvertisingReport( kComplete, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x4, 0x5, 0x6}), std::vector({0x1, 0x2, 0x3, 0x4, 0x5, 0x6})); // Test fragmented and truncated non scannable extended advertising. // The split may occur in the middle of a GAP entry. ASSERT_FALSE(reassembler_ .ProcessAdvertisingReport( kContinuation, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x1, 0x2, 0x3}) .has_value()); ASSERT_EQ( reassembler_.ProcessAdvertisingReport( kTruncated, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x4, 0x5, 0x6, 0x7}), std::vector({0x1, 0x2, 0x3, 0x4, 0x5, 0x6})); // Test fragmented and truncated anonymous, non scannable // extended advertising. The split may occur in the middle of a GAP entry. ASSERT_FALSE(reassembler_ .ProcessAdvertisingReport( kContinuation, (uint8_t)DirectAdvertisingAddressType::NO_ADDRESS_PROVIDED, Address::kEmpty, kSidNotPresent, {0x1, 0x2, 0x3}) .has_value()); ASSERT_EQ( reassembler_.ProcessAdvertisingReport( kTruncated, (uint8_t)DirectAdvertisingAddressType::NO_ADDRESS_PROVIDED, Address::kEmpty, kSidNotPresent, {0x4, 0x5, 0x6, 0x7}), std::vector({0x1, 0x2, 0x3, 0x4, 0x5, 0x6})); } TEST_F(LeScanningReassemblerTest, scannable_extended_advertising) { // Test fragmented scannable extended advertising. // The split may occur in the middle of a GAP entry. // Padding may occur at the end of the advertising data. ASSERT_FALSE(reassembler_ .ProcessAdvertisingReport( kScannable | kContinuation, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x1, 0x2, 0x3}) .has_value()); ASSERT_FALSE(reassembler_ .ProcessAdvertisingReport( kScannable | kComplete, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x4, 0x5, 0x6, 0x0, 0x0}) .has_value()); ASSERT_FALSE(reassembler_ .ProcessAdvertisingReport( kContinuation, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x7, 0x8, 0x9, 0xa}) .has_value()); ASSERT_EQ( reassembler_.ProcessAdvertisingReport( kTruncated, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0xb, 0xc, 0xd, 0xe, 0x0}), std::vector({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe})); } TEST_F(LeScanningReassemblerTest, ignore_scan_responses) { // Scan response without advertising data are ignored. ASSERT_FALSE(reassembler_ .ProcessAdvertisingReport( kScannable | kScanResponse | kComplete, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x1, 0x2}) .has_value()); ASSERT_EQ( reassembler_.ProcessAdvertisingReport( kComplete, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x1, 0x2}), std::vector({0x1, 0x2})); // The option ignore_scan_responses forces scan responses to be dropped. reassembler_.SetIgnoreScanResponses(true); ASSERT_EQ( reassembler_.ProcessAdvertisingReport( kScannable | kComplete, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x1, 0x2}), std::vector({0x1, 0x2})); } TEST_F(LeScanningReassemblerTest, interleaved_advertising) { // The reassembler must disambiguate advertising events by address, // address type, and SID. ASSERT_FALSE(reassembler_ .ProcessAdvertisingReport( kContinuation, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x2, 0x0}) .has_value()); ASSERT_FALSE(reassembler_ .ProcessAdvertisingReport( kContinuation, (uint8_t)AddressType::RANDOM_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x2, 0x1}) .has_value()); ASSERT_FALSE(reassembler_ .ProcessAdvertisingReport( kContinuation, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, 0x1, {0x2, 0x2}) .has_value()); ASSERT_FALSE(reassembler_ .ProcessAdvertisingReport( kContinuation, (uint8_t)DirectAdvertisingAddressType::NO_ADDRESS_PROVIDED, Address::kEmpty, 0x1, {0x2, 0x3}) .has_value()); ASSERT_EQ( reassembler_.ProcessAdvertisingReport( kComplete, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x0}), std::vector({0x2, 0x0, 0x0})); ASSERT_EQ( reassembler_.ProcessAdvertisingReport( kComplete, (uint8_t)AddressType::RANDOM_DEVICE_ADDRESS, kTestAddress, kSidNotPresent, {0x1}), std::vector({0x2, 0x1, 0x1})); ASSERT_EQ( reassembler_.ProcessAdvertisingReport( kComplete, (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS, kTestAddress, 0x1, {0x2}), std::vector({0x2, 0x2, 0x2})); ASSERT_EQ( reassembler_.ProcessAdvertisingReport( kComplete, (uint8_t)DirectAdvertisingAddressType::NO_ADDRESS_PROVIDED, Address::kEmpty, 0x1, {0x3}), std::vector({0x2, 0x3, 0x3})); } } // namespace bluetooth::hci