| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "helpers.h" |
| |
| #include <gtest/gtest.h> |
| |
| #include "adapter_test_fixture.h" |
| #include "fuchsia/bluetooth/control/cpp/fidl.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h" |
| #include "src/connectivity/bluetooth/core/bt-host/sm/types.h" |
| |
| namespace fble = fuchsia::bluetooth::le; |
| namespace fbt = fuchsia::bluetooth; |
| namespace fsys = fuchsia::bluetooth::sys; |
| namespace fbg = fuchsia::bluetooth::gatt; |
| |
| namespace bthost { |
| namespace fidl_helpers { |
| namespace { |
| |
| TEST(FidlHelpersTest, HostErrorToFidl) { |
| EXPECT_EQ(fsys::Error::FAILED, HostErrorToFidl(bt::HostError::kFailed)); |
| EXPECT_EQ(fsys::Error::TIMED_OUT, HostErrorToFidl(bt::HostError::kTimedOut)); |
| EXPECT_EQ(fsys::Error::INVALID_ARGUMENTS, HostErrorToFidl(bt::HostError::kInvalidParameters)); |
| EXPECT_EQ(fsys::Error::CANCELED, HostErrorToFidl(bt::HostError::kCanceled)); |
| EXPECT_EQ(fsys::Error::IN_PROGRESS, HostErrorToFidl(bt::HostError::kInProgress)); |
| EXPECT_EQ(fsys::Error::NOT_SUPPORTED, HostErrorToFidl(bt::HostError::kNotSupported)); |
| EXPECT_EQ(fsys::Error::PEER_NOT_FOUND, HostErrorToFidl(bt::HostError::kNotFound)); |
| |
| // All other errors currently map to FAILED. |
| EXPECT_EQ(fsys::Error::FAILED, HostErrorToFidl(bt::HostError::kProtocolError)); |
| } |
| |
| TEST(FidlHelpersTest, GattStatusToFidl) { |
| // Host errors |
| EXPECT_EQ(fbg::Error::INVALID_RESPONSE, |
| GattStatusToFidl(bt::att::Status(bt::HostError::kPacketMalformed))); |
| EXPECT_EQ(fbg::Error::FAILURE, GattStatusToFidl(bt::att::Status(bt::HostError::kTimedOut))); |
| |
| // Protocol errors |
| EXPECT_EQ(fbg::Error::INSUFFICIENT_AUTHORIZATION, |
| GattStatusToFidl(bt::att::Status(bt::att::ErrorCode::kInsufficientAuthorization))); |
| EXPECT_EQ(fbg::Error::INSUFFICIENT_AUTHENTICATION, |
| GattStatusToFidl(bt::att::Status(bt::att::ErrorCode::kInsufficientAuthentication))); |
| EXPECT_EQ(fbg::Error::INSUFFICIENT_ENCRYPTION_KEY_SIZE, |
| GattStatusToFidl(bt::att::Status(bt::att::ErrorCode::kInsufficientEncryptionKeySize))); |
| EXPECT_EQ(fbg::Error::INSUFFICIENT_ENCRYPTION, |
| GattStatusToFidl(bt::att::Status(bt::att::ErrorCode::kInsufficientEncryption))); |
| EXPECT_EQ(fbg::Error::READ_NOT_PERMITTED, |
| GattStatusToFidl(bt::att::Status(bt::att::ErrorCode::kReadNotPermitted))); |
| EXPECT_EQ(fbg::Error::FAILURE, |
| GattStatusToFidl(bt::att::Status(bt::att::ErrorCode::kUnlikelyError))); |
| } |
| |
| TEST(FIDL_HelpersTest, AddressBytesFrommString) { |
| EXPECT_FALSE(AddressBytesFromString("")); |
| EXPECT_FALSE(AddressBytesFromString("FF")); |
| EXPECT_FALSE(AddressBytesFromString("FF:FF:FF:FF:")); |
| EXPECT_FALSE(AddressBytesFromString("FF:FF:FF:FF:FF:F")); |
| EXPECT_FALSE(AddressBytesFromString("FF:FF:FF:FF:FF:FZ")); |
| EXPECT_FALSE(AddressBytesFromString("FF:FF:FF:FF:FF:FZ")); |
| EXPECT_FALSE(AddressBytesFromString("FF:FF:FF:FF:FF:FF ")); |
| EXPECT_FALSE(AddressBytesFromString(" FF:FF:FF:FF:FF:FF")); |
| |
| auto addr1 = AddressBytesFromString("FF:FF:FF:FF:FF:FF"); |
| EXPECT_TRUE(addr1); |
| EXPECT_EQ("FF:FF:FF:FF:FF:FF", addr1->ToString()); |
| |
| auto addr2 = AddressBytesFromString("03:7F:FF:02:0F:01"); |
| EXPECT_TRUE(addr2); |
| EXPECT_EQ("03:7F:FF:02:0F:01", addr2->ToString()); |
| } |
| |
| TEST(FIDL_HelpersTest, AdvertisingIntervalFromFidl) { |
| EXPECT_EQ(bt::gap::AdvertisingInterval::FAST1, |
| AdvertisingIntervalFromFidl(fble::AdvertisingModeHint::VERY_FAST)); |
| EXPECT_EQ(bt::gap::AdvertisingInterval::FAST2, |
| AdvertisingIntervalFromFidl(fble::AdvertisingModeHint::FAST)); |
| EXPECT_EQ(bt::gap::AdvertisingInterval::SLOW, |
| AdvertisingIntervalFromFidl(fble::AdvertisingModeHint::SLOW)); |
| } |
| |
| TEST(FIDL_HelpersTest, UuidFromFidl) { |
| fbt::Uuid input; |
| input.value = {{0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x0d, |
| 0x18, 0x00, 0x00}}; |
| |
| // We expect the input bytes to be carried over directly. |
| bt::UUID output = UuidFromFidl(input); |
| EXPECT_EQ("0000180d-0000-1000-8000-00805f9b34fb", output.ToString()); |
| EXPECT_EQ(2u, output.CompactSize()); |
| } |
| |
| TEST(FIDL_HelpersTest, AdvertisingDataFromFidlEmpty) { |
| fble::AdvertisingData input; |
| ASSERT_TRUE(input.IsEmpty()); |
| |
| auto output = AdvertisingDataFromFidl(input); |
| EXPECT_TRUE(output.service_uuids().empty()); |
| EXPECT_TRUE(output.service_data_uuids().empty()); |
| EXPECT_TRUE(output.manufacturer_data_ids().empty()); |
| EXPECT_TRUE(output.uris().empty()); |
| EXPECT_FALSE(output.appearance()); |
| EXPECT_FALSE(output.tx_power()); |
| EXPECT_FALSE(output.local_name()); |
| } |
| |
| TEST(FIDL_HelpersTest, AdvertisingDataFromFidlName) { |
| constexpr char kTestName[] = "💩"; |
| fble::AdvertisingData input; |
| input.set_name(kTestName); |
| |
| auto output = AdvertisingDataFromFidl(input); |
| EXPECT_TRUE(output.local_name()); |
| EXPECT_EQ(kTestName, *output.local_name()); |
| } |
| |
| TEST(FIDL_HelpersTest, AdvertisingDataFromFidlAppearance) { |
| fble::AdvertisingData input; |
| input.set_appearance(fuchsia::bluetooth::Appearance::HID_DIGITIZER_TABLET); |
| |
| auto output = AdvertisingDataFromFidl(input); |
| EXPECT_TRUE(output.appearance()); |
| |
| // Value comes from the standard Bluetooth "assigned numbers" document. |
| EXPECT_EQ(0x03C5, *output.appearance()); |
| } |
| |
| TEST(FIDL_HelpersTest, AdvertisingDataFromFidlTxPower) { |
| constexpr int8_t kTxPower = -50; |
| fble::AdvertisingData input; |
| input.set_tx_power_level(kTxPower); |
| |
| auto output = AdvertisingDataFromFidl(input); |
| EXPECT_TRUE(output.tx_power()); |
| EXPECT_EQ(kTxPower, *output.tx_power()); |
| } |
| |
| TEST(FIDL_HelpersTest, AdvertisingDataFromFidlUuids) { |
| // The first two entries are duplicated. The resulting structure should contain no duplicates. |
| const fbt::Uuid kUuid1{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}; |
| const fbt::Uuid kUuid2{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}; |
| const fbt::Uuid kUuid3{{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}}; |
| fble::AdvertisingData input; |
| input.set_service_uuids({{kUuid1, kUuid2, kUuid3}}); |
| |
| auto output = AdvertisingDataFromFidl(input); |
| EXPECT_EQ(2u, output.service_uuids().size()); |
| EXPECT_EQ(1u, output.service_uuids().count(bt::UUID(kUuid1.value))); |
| EXPECT_EQ(1u, output.service_uuids().count(bt::UUID(kUuid2.value))); |
| } |
| |
| TEST(FIDL_HelpersTest, AdvertisingDataFromFidlServiceData) { |
| const fbt::Uuid kUuid1{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}; |
| const fbt::Uuid kUuid2{{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}}; |
| const std::vector<uint8_t> kData1{{'h', 'e', 'l', 'l', 'o'}}; |
| const std::vector<uint8_t> kData2{{'b', 'y', 'e'}}; |
| |
| fble::AdvertisingData input; |
| input.set_service_data({{{kUuid1, kData1}, {kUuid2, kData2}}}); |
| |
| auto output = AdvertisingDataFromFidl(input); |
| EXPECT_EQ(2u, output.service_data_uuids().size()); |
| EXPECT_TRUE(ContainersEqual(bt::BufferView(kData1), output.service_data(bt::UUID(kUuid1.value)))); |
| EXPECT_TRUE(ContainersEqual(bt::BufferView(kData2), output.service_data(bt::UUID(kUuid2.value)))); |
| } |
| |
| TEST(FIDL_HelpersTest, AdvertisingDataFromFidlManufacturerData) { |
| constexpr uint16_t kCompanyId1 = 1; |
| constexpr uint16_t kCompanyId2 = 2; |
| const std::vector<uint8_t> kData1{{'h', 'e', 'l', 'l', 'o'}}; |
| const std::vector<uint8_t> kData2{{'b', 'y', 'e'}}; |
| |
| fble::AdvertisingData input; |
| input.set_manufacturer_data({{{kCompanyId1, kData1}, {kCompanyId2, kData2}}}); |
| |
| auto output = AdvertisingDataFromFidl(input); |
| EXPECT_EQ(2u, output.manufacturer_data_ids().size()); |
| EXPECT_TRUE(ContainersEqual(bt::BufferView(kData1), output.manufacturer_data(kCompanyId1))); |
| EXPECT_TRUE(ContainersEqual(bt::BufferView(kData2), output.manufacturer_data(kCompanyId2))); |
| } |
| |
| TEST(FIDL_HelpersTest, AdvertisingDataToFidlDeprecatedEmpty) { |
| bt::AdvertisingData input; |
| auto output = AdvertisingDataToFidlDeprecated(input); |
| |
| // All fields in |input| are not set. Therefore, output should have no set fields as well. |
| EXPECT_FALSE(output.name); |
| EXPECT_FALSE(output.tx_power_level); |
| EXPECT_FALSE(output.appearance); |
| EXPECT_FALSE(output.service_uuids); |
| EXPECT_FALSE(output.service_data); |
| EXPECT_FALSE(output.manufacturer_specific_data); |
| EXPECT_FALSE(output.solicited_service_uuids); |
| EXPECT_FALSE(output.uris); |
| } |
| |
| TEST(FIDL_HelpersTest, AdvertisingDataToFidlDeprecated) { |
| bt::AdvertisingData input; |
| input.SetLocalName("fuchsia"); |
| input.SetTxPower(4); |
| input.SetAppearance(0x1234); |
| |
| const uint16_t id = 0x5678; |
| const bt::UUID test_uuid = bt::UUID(id); |
| auto service_bytes = bt::CreateStaticByteBuffer(0x01, 0x02); |
| input.AddServiceUuid(test_uuid); |
| input.SetServiceData(test_uuid, service_bytes.view()); |
| |
| uint16_t company_id = 0x98; |
| auto manufacturer_bytes = bt::CreateStaticByteBuffer(0x04, 0x03); |
| input.SetManufacturerData(company_id, manufacturer_bytes.view()); |
| |
| auto uri = "http://fuchsia.cl"; |
| input.AddURI(uri); |
| |
| auto output = AdvertisingDataToFidlDeprecated(input); |
| |
| EXPECT_EQ("fuchsia", output.name); |
| |
| auto expected_power_level = fbt::Int8::New(); |
| expected_power_level->value = 4; |
| EXPECT_EQ(expected_power_level->value, output.tx_power_level->value); |
| |
| auto expected_appearance = fbt::UInt16::New(); |
| expected_appearance->value = 0x1234; |
| EXPECT_EQ(expected_appearance->value, output.appearance->value); |
| |
| EXPECT_EQ(1u, output.service_uuids->size()); |
| EXPECT_EQ(test_uuid.ToString(), output.service_uuids->front()); |
| |
| EXPECT_EQ(1u, output.service_data->size()); |
| auto service_data = output.service_data->front(); |
| EXPECT_EQ(test_uuid.ToString(), service_data.uuid); |
| EXPECT_TRUE(ContainersEqual(bt::BufferView(service_bytes), service_data.data)); |
| |
| EXPECT_EQ(1u, output.manufacturer_specific_data->size()); |
| auto manufacturer_data = output.manufacturer_specific_data->front(); |
| EXPECT_EQ(company_id, manufacturer_data.company_id); |
| EXPECT_TRUE(ContainersEqual(bt::BufferView(manufacturer_bytes), manufacturer_data.data)); |
| |
| EXPECT_EQ(1u, output.uris->size()); |
| EXPECT_EQ(uri, output.uris->front()); |
| } |
| |
| TEST(FIDL_HelpersTest, TechnologyTypeToFidl) { |
| EXPECT_EQ(fsys::TechnologyType::LOW_ENERGY, |
| TechnologyTypeToFidl(bt::gap::TechnologyType::kLowEnergy)); |
| EXPECT_EQ(fsys::TechnologyType::CLASSIC, TechnologyTypeToFidl(bt::gap::TechnologyType::kClassic)); |
| EXPECT_EQ(fsys::TechnologyType::DUAL_MODE, |
| TechnologyTypeToFidl(bt::gap::TechnologyType::kDualMode)); |
| } |
| |
| class FIDL_HelpersAdapterTest : public bthost::testing::AdapterTestFixture {}; |
| |
| TEST_F(FIDL_HelpersAdapterTest, HostInfoToFidl) { |
| // Verify that the default parameters are populated as expected. |
| auto host_info = HostInfoToFidl(*adapter()); |
| ASSERT_TRUE(host_info.has_id()); |
| ASSERT_TRUE(host_info.has_technology()); |
| ASSERT_TRUE(host_info.has_address()); |
| ASSERT_TRUE(host_info.has_local_name()); |
| ASSERT_TRUE(host_info.has_discoverable()); |
| ASSERT_TRUE(host_info.has_discovering()); |
| |
| EXPECT_EQ(adapter()->identifier().value(), host_info.id().value); |
| EXPECT_EQ(fsys::TechnologyType::DUAL_MODE, host_info.technology()); |
| EXPECT_EQ(fbt::AddressType::PUBLIC, host_info.address().type); |
| EXPECT_TRUE( |
| ContainersEqual(adapter()->state().controller_address().bytes(), host_info.address().bytes)); |
| EXPECT_EQ("fuchsia", host_info.local_name()); |
| EXPECT_FALSE(host_info.discoverable()); |
| EXPECT_FALSE(host_info.discovering()); |
| } |
| |
| TEST(FIDL_HelpersTest, SecurityLevelFromFidl) { |
| using FidlSecurityLevel = fuchsia::bluetooth::control::PairingSecurityLevel; |
| const FidlSecurityLevel level = FidlSecurityLevel::AUTHENTICATED; |
| EXPECT_EQ(bt::sm::SecurityLevel::kAuthenticated, SecurityLevelFromFidl(level)); |
| } |
| |
| TEST(FIDL_HelpersTest, SecurityLevelFromBadFidlFails) { |
| using FidlSecurityLevel = fuchsia::bluetooth::control::PairingSecurityLevel; |
| int nonexistant_security_level = 500000; |
| auto level = static_cast<FidlSecurityLevel>(nonexistant_security_level); |
| EXPECT_EQ(std::nullopt, SecurityLevelFromFidl(level)); |
| } |
| |
| TEST(FidlHelpersTest, PeerToFidlMandatoryFields) { |
| // Required by PeerCache expiry functions. |
| async::TestLoop dispatcher; |
| |
| inspect::Inspector inspector; |
| bt::gap::PeerCache cache(inspector.GetRoot().CreateChild(bt::gap::PeerCache::kInspectNodeName)); |
| bt::DeviceAddress addr(bt::DeviceAddress::Type::kLEPublic, {0, 1, 2, 3, 4, 5}); |
| auto* peer = cache.NewPeer(addr, /*connectable=*/true); |
| auto fidl = PeerToFidl(*peer); |
| ASSERT_TRUE(fidl.has_id()); |
| EXPECT_EQ(peer->identifier().value(), fidl.id().value); |
| ASSERT_TRUE(fidl.has_address()); |
| EXPECT_TRUE( |
| fidl::Equals(fbt::Address{fbt::AddressType::PUBLIC, {{0, 1, 2, 3, 4, 5}}}, fidl.address())); |
| ASSERT_TRUE(fidl.has_technology()); |
| EXPECT_EQ(fsys::TechnologyType::LOW_ENERGY, fidl.technology()); |
| ASSERT_TRUE(fidl.has_connected()); |
| EXPECT_FALSE(fidl.connected()); |
| ASSERT_TRUE(fidl.has_bonded()); |
| EXPECT_FALSE(fidl.bonded()); |
| |
| EXPECT_FALSE(fidl.has_name()); |
| EXPECT_FALSE(fidl.has_appearance()); |
| EXPECT_FALSE(fidl.has_rssi()); |
| EXPECT_FALSE(fidl.has_tx_power()); |
| EXPECT_FALSE(fidl.has_device_class()); |
| EXPECT_FALSE(fidl.has_services()); |
| } |
| |
| TEST(FidlHelpersTest, PeerToFidlOptionalFields) { |
| // Required by PeerCache expiry functions. |
| async::TestLoop dispatcher; |
| |
| const int8_t kRssi = 5; |
| const int8_t kTxPower = 6; |
| const auto kAdv = |
| bt::CreateStaticByteBuffer(0x02, 0x01, 0x01, // Flags: General Discoverable |
| 0x03, 0x19, 192, 0, // Appearance: Watch |
| 0x02, 0x0A, 0x06, // Tx-Power: 5 |
| 0x05, 0x09, 't', 'e', 's', 't' // Complete Local Name: "test" |
| ); |
| |
| inspect::Inspector inspector; |
| bt::gap::PeerCache cache(inspector.GetRoot().CreateChild(bt::gap::PeerCache::kInspectNodeName)); |
| bt::DeviceAddress addr(bt::DeviceAddress::Type::kLEPublic, {0, 1, 2, 3, 4, 5}); |
| auto* peer = cache.NewPeer(addr, /*connectable=*/true); |
| peer->MutLe().SetAdvertisingData(kRssi, kAdv); |
| peer->MutBrEdr().SetInquiryData(bt::hci::InquiryResult{ |
| bt::DeviceAddressBytes{{0, 1, 2, 3, 4, 5}}, bt::hci::PageScanRepetitionMode::kR0, 0, 0, |
| bt::DeviceClass(bt::DeviceClass::MajorClass::kPeripheral), 0}); |
| |
| auto fidl = PeerToFidl(*peer); |
| ASSERT_TRUE(fidl.has_name()); |
| EXPECT_EQ("test", fidl.name()); |
| ASSERT_TRUE(fidl.has_appearance()); |
| EXPECT_EQ(fbt::Appearance::WATCH, fidl.appearance()); |
| ASSERT_TRUE(fidl.has_rssi()); |
| EXPECT_EQ(kRssi, fidl.rssi()); |
| ASSERT_TRUE(fidl.has_tx_power()); |
| EXPECT_EQ(kTxPower, fidl.tx_power()); |
| ASSERT_TRUE(fidl.has_device_class()); |
| EXPECT_EQ(fbt::MAJOR_DEVICE_CLASS_PERIPHERAL, fidl.device_class().value); |
| |
| // TODO(fxb/37485) Add a test when this field gets populated. |
| EXPECT_FALSE(fidl.has_services()); |
| } |
| |
| TEST(FidlHelpersTest, ReliableModeFromFidl) { |
| using WriteOptions = fuchsia::bluetooth::gatt::WriteOptions; |
| using ReliableMode = fuchsia::bluetooth::gatt::ReliableMode; |
| WriteOptions options; |
| |
| // No options set, so this should default to disabled. |
| EXPECT_EQ(bt::gatt::ReliableMode::kDisabled, ReliableModeFromFidl(options)); |
| |
| options.set_reliable_mode(ReliableMode::ENABLED); |
| EXPECT_EQ(bt::gatt::ReliableMode::kEnabled, ReliableModeFromFidl(options)); |
| |
| options.set_reliable_mode(ReliableMode::DISABLED); |
| EXPECT_EQ(bt::gatt::ReliableMode::kDisabled, ReliableModeFromFidl(options)); |
| } |
| |
| } // namespace |
| } // namespace fidl_helpers |
| } // namespace bthost |