| // 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 <algorithm> |
| #include <iterator> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "adapter_test_fixture.h" |
| #include "fuchsia/bluetooth/cpp/fidl.h" |
| #include "fuchsia/bluetooth/le/cpp/fidl.h" |
| #include "fuchsia/bluetooth/sys/cpp/fidl.h" |
| #include "lib/fidl/cpp/comparison.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/advertising_data.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/uuid.h" |
| #include "src/connectivity/bluetooth/core/bt-host/gap/gap.h" |
| #include "src/connectivity/bluetooth/core/bt-host/sco/sco.h" |
| #include "src/connectivity/bluetooth/core/bt-host/sdp/sdp.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 fbredr = fuchsia::bluetooth::bredr; |
| namespace faudio = fuchsia::hardware::audio; |
| |
| namespace fuchsia::bluetooth { |
| // Make UUIDs equality comparable for advanced testing matchers. ADL rules mandate the namespace. |
| bool operator==(const Uuid& a, const Uuid& b) { return fidl::Equals(a, b); } |
| } // namespace fuchsia::bluetooth |
| |
| namespace bthost::fidl_helpers { |
| namespace { |
| |
| // Constants as BT stack types |
| const bt::UInt128 kTestKeyValue{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; |
| const bt::sm::SecurityProperties kTestSecurity(bt::sm::SecurityLevel::kSecureAuthenticated, 16, |
| true); |
| const bt::sm::LTK kTestLtk(kTestSecurity, bt::hci::LinkKey(kTestKeyValue, 0, 0)); |
| const bt::sm::Key kTestKey(kTestSecurity, kTestKeyValue); |
| |
| // Constants as FIDL types |
| const fbt::Address kPublicAddrFidl = fbt::Address{fbt::AddressType::PUBLIC, {1, 0, 0, 0, 0, 0}}; |
| const fbt::Address kRandomAddrFidl = fbt::Address{fbt::AddressType::RANDOM, {2, 0, 0, 0, 0, 0}}; |
| |
| const bt::DeviceAddress kTestPeerAddr(bt::DeviceAddress::Type::kBREDR, {1, 0, 0, 0, 0, 0}); |
| |
| const fsys::PeerKey kTestKeyFidl{ |
| .security = |
| fsys::SecurityProperties{ |
| .authenticated = true, |
| .secure_connections = true, |
| .encryption_key_size = 16, |
| }, |
| .data = fsys::Key{.value = kTestKeyValue}, |
| }; |
| const fsys::Ltk kTestLtkFidl{.key = kTestKeyFidl, .ediv = 0, .rand = 0}; |
| |
| TEST(FIDL_HelpersTest, 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(FIDL_HelpersTest, 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()); |
| |
| std::optional<bt::AdvertisingData> maybe_data = AdvertisingDataFromFidl(input); |
| ASSERT_TRUE(maybe_data.has_value()); |
| auto output = std::move(*maybe_data); |
| |
| 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); |
| |
| std::optional<bt::AdvertisingData> maybe_data = AdvertisingDataFromFidl(input); |
| ASSERT_TRUE(maybe_data.has_value()); |
| auto output = std::move(*maybe_data); |
| 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); |
| |
| std::optional<bt::AdvertisingData> maybe_data = AdvertisingDataFromFidl(input); |
| ASSERT_TRUE(maybe_data.has_value()); |
| auto output = std::move(*maybe_data); |
| |
| 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); |
| |
| std::optional<bt::AdvertisingData> maybe_data = AdvertisingDataFromFidl(input); |
| ASSERT_TRUE(maybe_data.has_value()); |
| auto output = std::move(*maybe_data); |
| |
| 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}}); |
| |
| std::optional<bt::AdvertisingData> maybe_data = AdvertisingDataFromFidl(input); |
| ASSERT_TRUE(maybe_data.has_value()); |
| auto output = std::move(*maybe_data); |
| |
| 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}}}); |
| |
| std::optional<bt::AdvertisingData> maybe_data = AdvertisingDataFromFidl(input); |
| ASSERT_TRUE(maybe_data.has_value()); |
| auto output = std::move(*maybe_data); |
| 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}}}); |
| |
| std::optional<bt::AdvertisingData> maybe_data = AdvertisingDataFromFidl(input); |
| ASSERT_TRUE(maybe_data.has_value()); |
| auto output = std::move(*maybe_data); |
| 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))); |
| } |
| |
| std::string UuidToString(fbt::Uuid uuid) { |
| std::string s; |
| for (uint8_t byte : uuid.value) { |
| s += std::to_string(static_cast<uint16_t>(byte)) + ", "; |
| } |
| return s; |
| } |
| // Each field for this test first attempts to perform the too-long conversion, and then verifies |
| // that the bounds are where expected by performing a successful conversion with a field that just |
| // fits in the encoded version. This also enables using the same `input` throughout the test. |
| TEST(FIDL_HelpersTest, AdvertisingDataFromFidlWithFieldsTooLong) { |
| fble::AdvertisingData input; |
| // The length of the AD name field must be <= 248 bytes per v5.2, Vol 4, Part E, 7.3.11 and Vol 3, |
| // Part C, 12.1.` |
| { |
| std::string name_that_fits(bt::kMaxNameLength, 'a'); |
| std::string too_long_name(bt::kMaxNameLength + 1, 'b'); |
| input.set_name(too_long_name); |
| EXPECT_FALSE(AdvertisingDataFromFidl(input).has_value()); |
| input.set_name(name_that_fits); |
| EXPECT_TRUE(AdvertisingDataFromFidl(input).has_value()); |
| } |
| { |
| // This is the longest encoding scheme known to Fuchsia BT, so this represents the longest |
| // string allowed (and subsequently, too long to be allowed) by both FIDL and internal |
| // invariants. |
| std::string uri = "ms-settings-cloudstorage:"; |
| uri += std::string(fble::MAX_URI_LENGTH - uri.size(), '.'); |
| input.set_uris({uri}); |
| EXPECT_FALSE(AdvertisingDataFromFidl(input).has_value()); |
| // This string should fit when it is one character shorter. |
| uri.pop_back(); |
| input.set_uris({uri}); |
| EXPECT_TRUE(AdvertisingDataFromFidl(input).has_value()); |
| } |
| // Ensure encoded service data that is too long is rejected. |
| { |
| const fbt::Uuid kUuid1{.value = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}; |
| // |kUuid1| = 16 bytes, i.e. 14 bytes longer than the shortest possible encoded UUID (2 bytes). |
| std::vector too_long_data(fble::MAX_SERVICE_DATA_LENGTH - 13, uint8_t{0xAB}); |
| input.set_service_data(std::vector({fble::ServiceData{.uuid = kUuid1, .data = too_long_data}})); |
| EXPECT_FALSE(AdvertisingDataFromFidl(input).has_value()); |
| // A vector that is 1 byte shorter than too_long_data should convert successfully |
| std::vector data_that_fits(too_long_data.size() - 1, too_long_data[0]); |
| input.set_service_data( |
| std::vector({fble::ServiceData{.uuid = kUuid1, .data = data_that_fits}})); |
| EXPECT_TRUE(AdvertisingDataFromFidl(input).has_value()); |
| } |
| // Ensure encoded manufacturer data that is too long is rejected. |
| { |
| uint16_t company_id = 0x1212; |
| std::vector too_long_data(fble::MAX_MANUFACTURER_DATA_LENGTH + 1, uint8_t{0xAB}); |
| input.set_manufacturer_data( |
| std::vector({fble::ManufacturerData{.company_id = company_id, .data = too_long_data}})); |
| EXPECT_FALSE(AdvertisingDataFromFidl(input).has_value()); |
| // A vector that is 1 byte shorter than too_long_data should convert successfully |
| std::vector data_that_fits(too_long_data.size() - 1, too_long_data[0]); |
| input.set_manufacturer_data( |
| std::vector({fble::ManufacturerData{.company_id = company_id, .data = data_that_fits}})); |
| EXPECT_TRUE(AdvertisingDataFromFidl(input).has_value()); |
| } |
| // Ensure input with too many service UUIDs is truncated (NOT rejected). |
| { |
| std::vector<fbt::Uuid> fbt_uuids; |
| fbt::Uuid kBaseUuid{.value = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}; |
| for (int i = 0; i < bt::kMax128BitUuids; ++i) { |
| fbt::Uuid next_uuid = kBaseUuid; |
| next_uuid.value[0] += i; |
| fbt_uuids.push_back(next_uuid); |
| } |
| input.set_service_uuids(fbt_uuids); |
| auto ad = AdvertisingDataFromFidl(input); |
| EXPECT_TRUE(ad.has_value()); |
| std::unordered_set<bt::UUID> converted_uuids = ad->service_uuids(); |
| for (auto& fbt_uuid : fbt_uuids) { |
| SCOPED_TRACE(UuidToString(fbt_uuid)); |
| EXPECT_TRUE(converted_uuids.find(bt::UUID(fbt_uuid.value)) != converted_uuids.end()); |
| } |
| fbt::Uuid excessive_uuid = kBaseUuid; |
| excessive_uuid.value[0] += bt::kMax128BitUuids + 1; |
| fbt_uuids.push_back(excessive_uuid); |
| input.set_service_uuids(fbt_uuids); |
| ad = AdvertisingDataFromFidl(input); |
| EXPECT_TRUE(ad.has_value()); |
| converted_uuids = ad->service_uuids(); |
| EXPECT_TRUE(converted_uuids.find(bt::UUID(excessive_uuid.value)) == converted_uuids.end()); |
| } |
| } |
| |
| 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; |
| EXPECT_TRUE(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); |
| EXPECT_TRUE(input.AddServiceUuid(test_uuid)); |
| EXPECT_TRUE(input.SetServiceData(test_uuid, service_bytes.view())); |
| |
| uint16_t company_id = 0x98; |
| auto manufacturer_bytes = bt::CreateStaticByteBuffer(0x04, 0x03); |
| EXPECT_TRUE(input.SetManufacturerData(company_id, manufacturer_bytes.view())); |
| |
| auto uri = "http://fuchsia.cl"; |
| EXPECT_TRUE(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, AdvertisingDataToFidlEmpty) { |
| bt::AdvertisingData input; |
| auto output = AdvertisingDataToFidl(input); |
| |
| // All fields in |input| are not set. Therefore, output should have no set fields as well. |
| EXPECT_FALSE(output.has_name()); |
| EXPECT_FALSE(output.has_tx_power_level()); |
| EXPECT_FALSE(output.has_appearance()); |
| EXPECT_FALSE(output.has_service_uuids()); |
| EXPECT_FALSE(output.has_service_data()); |
| EXPECT_FALSE(output.has_manufacturer_data()); |
| EXPECT_FALSE(output.has_uris()); |
| } |
| |
| TEST(FIDL_HelpersTest, AdvertisingDataToFidl) { |
| bt::AdvertisingData input; |
| EXPECT_TRUE(input.SetLocalName("fuchsia")); |
| input.SetTxPower(4); |
| const uint16_t kAppearance = 193u; // WATCH_SPORTS |
| input.SetAppearance(kAppearance); |
| |
| const uint16_t id = 0x5678; |
| const bt::UUID test_uuid = bt::UUID(id); |
| auto service_bytes = bt::CreateStaticByteBuffer(0x01, 0x02); |
| EXPECT_TRUE(input.AddServiceUuid(test_uuid)); |
| EXPECT_TRUE(input.SetServiceData(test_uuid, service_bytes.view())); |
| |
| uint16_t company_id = 0x98; |
| auto manufacturer_bytes = bt::CreateStaticByteBuffer(0x04, 0x03); |
| EXPECT_TRUE(input.SetManufacturerData(company_id, manufacturer_bytes.view())); |
| |
| auto uri = "http://fuchsia.cl/461435"; |
| EXPECT_TRUE(input.AddUri(uri)); |
| |
| auto output = AdvertisingDataToFidl(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()); |
| |
| EXPECT_EQ(fbt::Appearance{kAppearance}, output.appearance()); |
| |
| EXPECT_EQ(1u, output.service_uuids().size()); |
| EXPECT_EQ(test_uuid, UuidFromFidl(output.service_uuids().front())); |
| |
| EXPECT_EQ(1u, output.service_data().size()); |
| auto service_data = output.service_data().front(); |
| EXPECT_EQ(test_uuid, UuidFromFidl(service_data.uuid)); |
| EXPECT_TRUE(ContainersEqual(bt::BufferView(service_bytes), service_data.data)); |
| |
| EXPECT_EQ(1u, output.manufacturer_data().size()); |
| auto manufacturer_data = output.manufacturer_data().front(); |
| EXPECT_EQ(company_id, manufacturer_data.company_id); |
| EXPECT_TRUE(ContainersEqual(bt::BufferView(manufacturer_bytes), manufacturer_data.data)); |
| |
| EXPECT_THAT(output.uris(), ::testing::ElementsAre(uri)); |
| } |
| |
| TEST(FIDL_HelpersTest, AdvertisingDataToFidlOmitsNonEnumeratedAppearance) { |
| // There is an "unknown" appearance, which is why this isn't named that. |
| const uint16_t kNonEnumeratedAppearance = 0xFFFFu; |
| bt::AdvertisingData input; |
| input.SetAppearance(kNonEnumeratedAppearance); |
| |
| EXPECT_FALSE(AdvertisingDataToFidl(input).has_appearance()); |
| |
| const uint16_t kKnownAppearance = 832u; // HEART_RATE_SENSOR |
| input.SetAppearance(kKnownAppearance); |
| |
| EXPECT_TRUE(AdvertisingDataToFidl(input).has_appearance()); |
| } |
| |
| TEST(FIDL_HelpersTest, LeSecurityModeFromFidl) { |
| EXPECT_EQ(bt::gap::LeSecurityMode::Mode1, LeSecurityModeFromFidl(fsys::LeSecurityMode::MODE_1)); |
| EXPECT_EQ(bt::gap::LeSecurityMode::SecureConnectionsOnly, |
| LeSecurityModeFromFidl(fsys::LeSecurityMode::SECURE_CONNECTIONS_ONLY)); |
| auto nonexistent_security_mode = static_cast<fsys::LeSecurityMode>(0xFF); |
| EXPECT_EQ(bt::gap::LeSecurityMode::SecureConnectionsOnly, |
| LeSecurityModeFromFidl(nonexistent_security_mode)); |
| } |
| |
| 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)); |
| } |
| |
| TEST(FIDL_HelpersTest, SecurityLevelFromFidl) { |
| const fsys::PairingSecurityLevel level = fsys::PairingSecurityLevel::AUTHENTICATED; |
| EXPECT_EQ(bt::sm::SecurityLevel::kAuthenticated, SecurityLevelFromFidl(level)); |
| } |
| |
| TEST(FIDL_HelpersTest, SecurityLevelFromBadFidlFails) { |
| int nonexistant_security_level = 500000; |
| auto level = static_cast<fsys::PairingSecurityLevel>(nonexistant_security_level); |
| EXPECT_EQ(std::nullopt, SecurityLevelFromFidl(level)); |
| } |
| |
| TEST(FIDL_HelpersTest, PeerToFidlMandatoryFields) { |
| // Required by PeerCache expiry functions. |
| async::TestLoop dispatcher; |
| |
| inspect::Inspector inspector; |
| bt::gap::PeerCache cache; |
| 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()); |
| EXPECT_FALSE(fidl.has_le_services()); |
| EXPECT_FALSE(fidl.has_bredr_services()); |
| } |
| |
| TEST(FIDL_HelpersTest, 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" |
| ); |
| const std::vector kBrEdrServices = {bt::UUID(uint16_t{0x110a}), bt::UUID(uint16_t{0x110b})}; |
| |
| inspect::Inspector inspector; |
| bt::gap::PeerCache cache; |
| 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}); |
| for (auto& service : kBrEdrServices) { |
| peer->MutBrEdr().AddService(service); |
| } |
| |
| 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); |
| |
| // Deprecated and never implemented (see fxbug.dev/57344). |
| EXPECT_FALSE(fidl.has_services()); |
| |
| // TODO(fxbug.dev/57344): Add a test when this field gets populated. |
| EXPECT_FALSE(fidl.has_le_services()); |
| |
| ASSERT_TRUE(fidl.has_bredr_services()); |
| std::vector<fbt::Uuid> expected_uuids; |
| std::transform(kBrEdrServices.begin(), kBrEdrServices.end(), std::back_inserter(expected_uuids), |
| UuidToFidl); |
| EXPECT_THAT(fidl.bredr_services(), ::testing::UnorderedElementsAreArray(expected_uuids)); |
| } |
| |
| TEST(FIDL_HelpersTest, 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)); |
| } |
| |
| // TODO: Set information w/o setting language, set a FIDL type that cannot be converted |
| // - make sure the expected attributes are set and have the correct type |
| // - make sure the profile descriptor sets the right attributes |
| TEST(FIDL_HelpersTest, ServiceDefinitionToServiceRecord) { |
| fuchsia::bluetooth::bredr::ServiceDefinition def_should_fail; |
| // Should fail to convert without service class UUIDs. |
| auto rec_no_uuids = ServiceDefinitionToServiceRecord(def_should_fail); |
| EXPECT_FALSE(rec_no_uuids.is_ok()); |
| // Should fail to convert when information set without language. |
| def_should_fail.mutable_service_class_uuids()->emplace_back( |
| fidl_helpers::UuidToFidl(bt::sdp::profile::kAudioSink)); |
| fuchsia::bluetooth::bredr::Information info_no_language; |
| def_should_fail.mutable_information()->emplace_back(std::move(info_no_language)); |
| auto rec_no_language = ServiceDefinitionToServiceRecord(def_should_fail); |
| EXPECT_FALSE(rec_no_language.is_ok()); |
| |
| // Create definition for successful conversion. |
| fuchsia::bluetooth::bredr::ServiceDefinition def; |
| def.mutable_service_class_uuids()->emplace_back( |
| fidl_helpers::UuidToFidl(bt::sdp::profile::kAudioSink)); |
| fuchsia::bluetooth::bredr::Information info; |
| info.set_language("en"); |
| info.set_name("TEST"); |
| def.mutable_information()->emplace_back(std::move(info)); |
| fuchsia::bluetooth::bredr::ProtocolDescriptor l2cap_proto; |
| l2cap_proto.protocol = fuchsia::bluetooth::bredr::ProtocolIdentifier::L2CAP; |
| fuchsia::bluetooth::bredr::DataElement l2cap_data_el; |
| l2cap_data_el.set_uint16(fuchsia::bluetooth::bredr::PSM_SDP); |
| l2cap_proto.params.emplace_back(std::move(l2cap_data_el)); |
| def.mutable_protocol_descriptor_list()->emplace_back(std::move(l2cap_proto)); |
| fuchsia::bluetooth::bredr::ProtocolDescriptor avdtp_proto; |
| avdtp_proto.protocol = fuchsia::bluetooth::bredr::ProtocolIdentifier::AVDTP; |
| fuchsia::bluetooth::bredr::DataElement avdtp_data_el; |
| avdtp_data_el.set_uint16(0x0103); // Version 1.3 |
| avdtp_proto.params.emplace_back(std::move(avdtp_data_el)); |
| def.mutable_protocol_descriptor_list()->emplace_back(std::move(avdtp_proto)); |
| fuchsia::bluetooth::bredr::ProfileDescriptor prof_desc; |
| prof_desc.profile_id = |
| fuchsia::bluetooth::bredr::ServiceClassProfileIdentifier::ADVANCED_AUDIO_DISTRIBUTION; |
| prof_desc.major_version = 1; |
| prof_desc.minor_version = 3; |
| def.mutable_profile_descriptors()->emplace_back(prof_desc); |
| bt::sdp::AttributeId valid_att_id = 0x1111; |
| fuchsia::bluetooth::bredr::Attribute valid_attribute; |
| valid_attribute.element.set_uint8(0x01); |
| valid_attribute.id = valid_att_id; |
| def.mutable_additional_attributes()->emplace_back(std::move(valid_attribute)); |
| // Add an invalid additional attribute that should not convert. |
| bt::sdp::AttributeId invalid_att_id = 0x1112; |
| fuchsia::bluetooth::bredr::Attribute invalid_attribute; |
| invalid_attribute.element.set_url(""); |
| invalid_attribute.id = invalid_att_id; |
| def.mutable_additional_attributes()->emplace_back(std::move(invalid_attribute)); |
| |
| // Confirm converted ServiceRecord fields match ServiceDefinition |
| auto rec = ServiceDefinitionToServiceRecord(def); |
| EXPECT_TRUE(rec.is_ok()); |
| |
| // Confirm UUIDs match |
| std::unordered_set<bt::UUID> attribute_uuid = {bt::sdp::profile::kAudioSink}; |
| EXPECT_TRUE(rec.value().FindUUID(attribute_uuid)); |
| |
| // Confirm information fields match |
| EXPECT_TRUE(rec.value().HasAttribute(bt::sdp::kLanguageBaseAttributeIdList)); |
| const bt::sdp::DataElement& lang_val = |
| rec.value().GetAttribute(bt::sdp::kLanguageBaseAttributeIdList); |
| auto triplets = lang_val.Get<std::vector<bt::sdp::DataElement>>(); |
| EXPECT_TRUE(triplets); |
| EXPECT_TRUE(triplets->size() % 3 == 0); |
| EXPECT_EQ(bt::sdp::DataElement::Type::kUnsignedInt, triplets->at(0).type()); |
| EXPECT_EQ(bt::sdp::DataElement::Type::kUnsignedInt, triplets->at(1).type()); |
| EXPECT_EQ(bt::sdp::DataElement::Type::kUnsignedInt, triplets->at(2).type()); |
| auto lang = triplets->at(0).Get<uint16_t>(); |
| EXPECT_TRUE(lang); |
| EXPECT_EQ(0x656e, *lang); // should be 'en' in ascii (but big-endian) |
| auto encoding = triplets->at(1).Get<uint16_t>(); |
| EXPECT_TRUE(encoding); |
| EXPECT_EQ(106, *encoding); // should always be UTF-8 |
| auto base_attrid = triplets->at(2).Get<uint16_t>(); |
| EXPECT_TRUE(base_attrid); |
| EXPECT_EQ(0x0100, *base_attrid); // The primary language must be at 0x0100. |
| EXPECT_TRUE(rec.value().HasAttribute(*base_attrid + bt::sdp::kServiceNameOffset)); |
| const bt::sdp::DataElement& name_elem = |
| rec.value().GetAttribute(*base_attrid + bt::sdp::kServiceNameOffset); |
| auto name = name_elem.Get<std::string>(); |
| EXPECT_TRUE(name); |
| EXPECT_EQ("TEST", *name); |
| |
| // Confirm protocol + descriptor list |
| EXPECT_TRUE(rec.value().HasAttribute(bt::sdp::kProtocolDescriptorList)); |
| const bt::sdp::DataElement& protocol_val = |
| rec.value().GetAttribute(bt::sdp::kProtocolDescriptorList); |
| bt::DynamicByteBuffer protocol_block(protocol_val.WriteSize()); |
| protocol_val.Write(&protocol_block); |
| auto expected_protocol_list = |
| bt::CreateStaticByteBuffer(0x35, 0x10, // Data Element Sequence (10 bytes) |
| 0x35, 0x06, // Data Element Sequence (6 bytes) |
| 0x19, // UUID (16 bits) |
| 0x01, 0x00, // L2CAP Profile UUID |
| 0x09, // uint16_t |
| 0x00, 0x01, // PSM = SDP |
| 0x35, 0x06, // Data Element Sequence (6 bytes) |
| 0x19, // UUID |
| 0x00, 0x19, // AVTDP Profile UUID |
| 0x09, // uint16_t |
| 0x01, 0x03 // PSM_AVDTP |
| ); |
| EXPECT_EQ(expected_protocol_list.size(), protocol_block.size()); |
| EXPECT_TRUE(ContainersEqual(expected_protocol_list, protocol_block)); |
| |
| // Confirm profile descriptor list |
| EXPECT_TRUE(rec.value().HasAttribute(bt::sdp::kBluetoothProfileDescriptorList)); |
| const bt::sdp::DataElement& profile_val = |
| rec.value().GetAttribute(bt::sdp::kBluetoothProfileDescriptorList); |
| bt::DynamicByteBuffer profile_block(profile_val.WriteSize()); |
| profile_val.Write(&profile_block); |
| auto expected_profile_list = |
| bt::CreateStaticByteBuffer(0x35, 0x08, // Data Element Sequence (8 bytes) |
| 0x35, 0x06, // Data Element Sequence (6 bytes) |
| 0x19, // UUID |
| 0x11, 0x0d, // Advanced Audio Identifier |
| 0x09, // uint16_t |
| 0x01, 0x03 // Major and minor version |
| ); |
| EXPECT_EQ(expected_profile_list.size(), profile_block.size()); |
| EXPECT_TRUE(ContainersEqual(expected_profile_list, profile_block)); |
| |
| // Confirm additional attributes |
| EXPECT_TRUE(rec.value().HasAttribute(valid_att_id)); |
| EXPECT_FALSE(rec.value().HasAttribute(invalid_att_id)); |
| } |
| |
| TEST(FIDL_HelpersTest, FidlToBrEdrSecurityRequirements) { |
| fbredr::ChannelParameters params; |
| EXPECT_EQ( |
| FidlToBrEdrSecurityRequirements(params), |
| (bt::gap::BrEdrSecurityRequirements{.authentication = false, .secure_connections = false})); |
| |
| params.set_security_requirements(fbredr::SecurityRequirements()); |
| EXPECT_EQ( |
| FidlToBrEdrSecurityRequirements(params), |
| (bt::gap::BrEdrSecurityRequirements{.authentication = false, .secure_connections = false})); |
| |
| params.mutable_security_requirements()->set_secure_connections_required(false); |
| EXPECT_EQ( |
| FidlToBrEdrSecurityRequirements(params), |
| (bt::gap::BrEdrSecurityRequirements{.authentication = false, .secure_connections = false})); |
| params.mutable_security_requirements()->clear_secure_connections_required(); |
| |
| params.mutable_security_requirements()->set_authentication_required(false); |
| EXPECT_EQ( |
| FidlToBrEdrSecurityRequirements(params), |
| (bt::gap::BrEdrSecurityRequirements{.authentication = false, .secure_connections = false})); |
| |
| params.mutable_security_requirements()->set_secure_connections_required(false); |
| EXPECT_EQ( |
| FidlToBrEdrSecurityRequirements(params), |
| (bt::gap::BrEdrSecurityRequirements{.authentication = false, .secure_connections = false})); |
| |
| params.mutable_security_requirements()->set_authentication_required(true); |
| EXPECT_EQ( |
| FidlToBrEdrSecurityRequirements(params), |
| (bt::gap::BrEdrSecurityRequirements{.authentication = true, .secure_connections = false})); |
| |
| params.mutable_security_requirements()->set_secure_connections_required(true); |
| EXPECT_EQ( |
| FidlToBrEdrSecurityRequirements(params), |
| (bt::gap::BrEdrSecurityRequirements{.authentication = true, .secure_connections = true})); |
| } |
| |
| TEST(FIDL_HelpersTest, AddressFromFidlBondingDataRandomAddressRejectedIfBredr) { |
| fsys::BondingData bond; |
| bond.set_address(kRandomAddrFidl); |
| bond.set_bredr_bond(fsys::BredrBondData()); |
| |
| EXPECT_EQ(std::nullopt, AddressFromFidlBondingData(bond)); |
| } |
| |
| TEST(FIDL_HelpersTest, AddressFromFidlBondingDataBredr) { |
| fsys::BondingData bond; |
| bond.set_address(kPublicAddrFidl); |
| bond.set_bredr_bond(fsys::BredrBondData()); |
| |
| auto addr = AddressFromFidlBondingData(bond); |
| ASSERT_TRUE(addr); |
| EXPECT_EQ(addr->type(), bt::DeviceAddress::Type::kBREDR); |
| } |
| |
| TEST(FIDL_HelpersTest, AddressFromFidlBondingDataDualMode) { |
| fsys::BondingData bond; |
| bond.set_address(kPublicAddrFidl); |
| bond.set_bredr_bond(fsys::BredrBondData()); |
| bond.set_le_bond(fsys::LeBondData()); |
| |
| auto addr = AddressFromFidlBondingData(bond); |
| ASSERT_TRUE(addr); |
| EXPECT_EQ(addr->type(), bt::DeviceAddress::Type::kBREDR); |
| } |
| |
| TEST(FIDL_HelpersTest, AddressFromFidlBondingDataLePublic) { |
| fsys::BondingData bond; |
| bond.set_address(kPublicAddrFidl); |
| bond.set_le_bond(fsys::LeBondData()); |
| |
| auto addr = AddressFromFidlBondingData(bond); |
| ASSERT_TRUE(addr); |
| EXPECT_EQ(addr->type(), bt::DeviceAddress::Type::kLEPublic); |
| } |
| |
| TEST(FIDL_HelpersTest, AddressFromFidlBondingDataLeRandom) { |
| fsys::BondingData bond; |
| bond.set_address(kRandomAddrFidl); |
| bond.set_le_bond(fsys::LeBondData()); |
| |
| auto addr = AddressFromFidlBondingData(bond); |
| ASSERT_TRUE(addr); |
| EXPECT_EQ(addr->type(), bt::DeviceAddress::Type::kLERandom); |
| } |
| |
| TEST(FIDL_HelpersTest, LePairingDataFromFidlEmpty) { |
| bt::sm::PairingData result = LePairingDataFromFidl(fsys::LeBondData()); |
| EXPECT_FALSE(result.identity_address); |
| EXPECT_FALSE(result.local_ltk); |
| EXPECT_FALSE(result.peer_ltk); |
| EXPECT_FALSE(result.irk); |
| EXPECT_FALSE(result.csrk); |
| } |
| |
| TEST(FIDL_HelpersTest, LePairingDataFromFidl) { |
| fsys::LeBondData le; |
| le.set_local_ltk(kTestLtkFidl); |
| le.set_peer_ltk(kTestLtkFidl); |
| le.set_irk(kTestKeyFidl); |
| le.set_csrk(kTestKeyFidl); |
| |
| bt::sm::PairingData result = LePairingDataFromFidl(std::move(le)); |
| EXPECT_FALSE(result.identity_address); |
| ASSERT_TRUE(result.local_ltk); |
| ASSERT_TRUE(result.peer_ltk); |
| ASSERT_TRUE(result.irk); |
| ASSERT_TRUE(result.csrk); |
| |
| EXPECT_EQ(kTestLtk, *result.local_ltk); |
| EXPECT_EQ(kTestLtk, *result.peer_ltk); |
| EXPECT_EQ(kTestKey, *result.irk); |
| EXPECT_EQ(kTestKey, *result.csrk); |
| } |
| |
| TEST(FIDL_HelpersTest, BredrKeyFromFidlEmpty) { |
| EXPECT_FALSE(BredrKeyFromFidl(fsys::BredrBondData())); |
| } |
| |
| TEST(FIDL_HelpersTest, BredrKeyFromFidl) { |
| const bt::sm::SecurityProperties kTestSecurity(bt::sm::SecurityLevel::kSecureAuthenticated, 16, |
| true); |
| const bt::sm::LTK kTestLtk(kTestSecurity, bt::hci::LinkKey(kTestKeyValue, 0, 0)); |
| |
| fsys::BredrBondData bredr; |
| bredr.set_link_key(kTestKeyFidl); |
| std::optional<bt::sm::LTK> result = BredrKeyFromFidl(bredr); |
| ASSERT_TRUE(result); |
| EXPECT_EQ(kTestLtk, *result); |
| } |
| |
| TEST(FIDL_HelpersTest, BredrServicesFromFidlEmpty) { |
| EXPECT_TRUE(BredrServicesFromFidl(fsys::BredrBondData()).empty()); |
| } |
| |
| TEST(FIDL_HelpersTest, BredrServicesFromFidl) { |
| fsys::BredrBondData bredr; |
| bredr.mutable_services()->push_back(UuidToFidl(bt::sdp::profile::kAudioSink)); |
| bredr.mutable_services()->push_back(UuidToFidl(bt::sdp::profile::kAudioSource)); |
| auto bredr_services = BredrServicesFromFidl(bredr); |
| EXPECT_THAT(bredr_services, ::testing::UnorderedElementsAre(bt::sdp::profile::kAudioSource, |
| bt::sdp::profile::kAudioSink)); |
| } |
| |
| 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_F(FIDL_HelpersAdapterTest, PeerToFidlBondingData_NoTransportData) { |
| auto* peer = adapter()->peer_cache()->NewPeer(kTestPeerAddr, /*connectable=*/true); |
| fsys::BondingData data = PeerToFidlBondingData(*adapter(), *peer); |
| ASSERT_TRUE(data.has_identifier()); |
| ASSERT_TRUE(data.has_local_address()); |
| ASSERT_TRUE(data.has_address()); |
| EXPECT_FALSE(data.has_name()); |
| EXPECT_FALSE(data.has_le_bond()); |
| EXPECT_FALSE(data.has_bredr_bond()); |
| |
| EXPECT_EQ(peer->identifier().value(), data.identifier().value); |
| EXPECT_TRUE(fidl::Equals((fbt::Address{fbt::AddressType::PUBLIC, std::array<uint8_t, 6>{0}}), |
| data.local_address())); |
| EXPECT_TRUE(fidl::Equals(kPublicAddrFidl, data.address())); |
| } |
| |
| TEST_F(FIDL_HelpersAdapterTest, PeerToFidlBondingData_BothTransportsPresentButNotBonded) { |
| auto* peer = adapter()->peer_cache()->NewPeer(kTestPeerAddr, /*connectable=*/true); |
| peer->MutLe(); |
| peer->MutBrEdr(); |
| |
| fsys::BondingData data = PeerToFidlBondingData(*adapter(), *peer); |
| ASSERT_TRUE(data.has_identifier()); |
| ASSERT_TRUE(data.has_local_address()); |
| ASSERT_TRUE(data.has_address()); |
| EXPECT_FALSE(data.has_le_bond()); |
| EXPECT_FALSE(data.has_bredr_bond()); |
| |
| EXPECT_EQ(peer->identifier().value(), data.identifier().value); |
| EXPECT_TRUE(fidl::Equals((fbt::Address{fbt::AddressType::PUBLIC, std::array<uint8_t, 6>{0}}), |
| data.local_address())); |
| EXPECT_TRUE(fidl::Equals(kPublicAddrFidl, data.address())); |
| } |
| |
| TEST_F(FIDL_HelpersAdapterTest, PeerToFidlBondingData_BredrServicesDiscoveredNotBonded) { |
| auto* peer = adapter()->peer_cache()->NewPeer(kTestPeerAddr, /*connectable=*/true); |
| peer->MutBrEdr().AddService(bt::UUID(uint16_t{0x1234})); |
| |
| fsys::BondingData data = PeerToFidlBondingData(*adapter(), *peer); |
| EXPECT_FALSE(data.has_bredr_bond()); |
| } |
| |
| TEST_F(FIDL_HelpersAdapterTest, PeerToFidlBondingData_EmptyLeData) { |
| auto* peer = adapter()->peer_cache()->NewPeer(kTestPeerAddr, /*connectable=*/true); |
| peer->MutLe().SetBondData(bt::sm::PairingData()); |
| |
| fsys::BondingData data = PeerToFidlBondingData(*adapter(), *peer); |
| EXPECT_FALSE(data.has_bredr_bond()); |
| ASSERT_TRUE(data.has_le_bond()); |
| EXPECT_FALSE(data.le_bond().has_local_ltk()); |
| EXPECT_FALSE(data.le_bond().has_peer_ltk()); |
| EXPECT_FALSE(data.le_bond().has_irk()); |
| EXPECT_FALSE(data.le_bond().has_csrk()); |
| } |
| |
| TEST_F(FIDL_HelpersAdapterTest, PeerToFidlBondingData_LeData) { |
| auto* peer = adapter()->peer_cache()->NewPeer(kTestPeerAddr, /*connectable=*/true); |
| peer->MutLe().SetBondData(bt::sm::PairingData{ |
| .local_ltk = {kTestLtk}, |
| .peer_ltk = {kTestLtk}, |
| .irk = {kTestKey}, |
| .csrk = {kTestKey}, |
| }); |
| |
| fsys::BondingData data = PeerToFidlBondingData(*adapter(), *peer); |
| EXPECT_FALSE(data.has_bredr_bond()); |
| ASSERT_TRUE(data.has_le_bond()); |
| ASSERT_TRUE(data.le_bond().has_local_ltk()); |
| ASSERT_TRUE(data.le_bond().has_peer_ltk()); |
| ASSERT_TRUE(data.le_bond().has_irk()); |
| ASSERT_TRUE(data.le_bond().has_csrk()); |
| |
| EXPECT_TRUE(fidl::Equals(kTestLtkFidl, data.le_bond().local_ltk())); |
| EXPECT_TRUE(fidl::Equals(kTestLtkFidl, data.le_bond().peer_ltk())); |
| EXPECT_TRUE(fidl::Equals(kTestKeyFidl, data.le_bond().irk())); |
| EXPECT_TRUE(fidl::Equals(kTestKeyFidl, data.le_bond().csrk())); |
| } |
| |
| TEST_F(FIDL_HelpersAdapterTest, PeerToFidlBondingData_BredrData) { |
| auto* peer = adapter()->peer_cache()->NewPeer(kTestPeerAddr, /*connectable=*/true); |
| peer->MutBrEdr().SetBondData(kTestLtk); |
| |
| fsys::BondingData data = PeerToFidlBondingData(*adapter(), *peer); |
| EXPECT_FALSE(data.has_le_bond()); |
| ASSERT_TRUE(data.has_bredr_bond()); |
| ASSERT_TRUE(data.bredr_bond().has_link_key()); |
| EXPECT_TRUE(fidl::Equals(kTestKeyFidl, data.bredr_bond().link_key())); |
| } |
| |
| TEST_F(FIDL_HelpersAdapterTest, PeerToFidlBondingData_IncludesBredrServices) { |
| auto* peer = adapter()->peer_cache()->NewPeer(kTestPeerAddr, /*connectable=*/true); |
| peer->MutBrEdr().SetBondData(kTestLtk); |
| peer->MutBrEdr().AddService(bt::sdp::profile::kAudioSink); |
| peer->MutBrEdr().AddService(bt::sdp::profile::kAudioSource); |
| |
| fsys::BondingData data = PeerToFidlBondingData(*adapter(), *peer); |
| ASSERT_TRUE(data.has_bredr_bond()); |
| ASSERT_TRUE(data.bredr_bond().has_services()); |
| |
| EXPECT_THAT(data.bredr_bond().services(), |
| ::testing::UnorderedElementsAre(UuidToFidl(bt::sdp::profile::kAudioSink), |
| UuidToFidl(bt::sdp::profile::kAudioSource))); |
| } |
| |
| TEST_F(FIDL_HelpersAdapterTest, FidlToScoParameters) { |
| fbredr::ScoConnectionParameters params; |
| EXPECT_TRUE(FidlToScoParameters(params).is_error()); |
| params.set_parameter_set(fbredr::HfpParameterSet::MSBC_T2); |
| EXPECT_TRUE(FidlToScoParameters(params).is_error()); |
| params.set_air_coding_format(fbredr::CodingFormat::MSBC); |
| EXPECT_TRUE(FidlToScoParameters(params).is_error()); |
| params.set_air_frame_size(8u); |
| EXPECT_TRUE(FidlToScoParameters(params).is_error()); |
| params.set_io_bandwidth(32000); |
| EXPECT_TRUE(FidlToScoParameters(params).is_error()); |
| params.set_io_coding_format(fbredr::CodingFormat::LINEAR_PCM); |
| EXPECT_TRUE(FidlToScoParameters(params).is_error()); |
| params.set_io_frame_size(16u); |
| EXPECT_TRUE(FidlToScoParameters(params).is_error()); |
| params.set_io_pcm_data_format(faudio::SampleFormat::PCM_SIGNED); |
| EXPECT_TRUE(FidlToScoParameters(params).is_error()); |
| params.set_io_pcm_sample_payload_msb_position(3u); |
| EXPECT_TRUE(FidlToScoParameters(params).is_error()); |
| params.set_path(fbredr::DataPath::OFFLOAD); |
| ASSERT_TRUE(FidlToScoParameters(params).is_ok()); |
| |
| bt::hci::SynchronousConnectionParameters out = FidlToScoParameters(params).take_value(); |
| EXPECT_EQ(out.transmit_bandwidth, 8000u); |
| EXPECT_EQ(out.receive_bandwidth, 8000u); |
| |
| EXPECT_EQ(out.transmit_coding_format.coding_format, bt::hci::CodingFormat::kMSbc); |
| EXPECT_EQ(out.transmit_coding_format.company_id, 0u); |
| EXPECT_EQ(out.transmit_coding_format.vendor_codec_id, 0u); |
| |
| EXPECT_EQ(out.receive_coding_format.coding_format, bt::hci::CodingFormat::kMSbc); |
| EXPECT_EQ(out.receive_coding_format.company_id, 0u); |
| EXPECT_EQ(out.receive_coding_format.vendor_codec_id, 0u); |
| |
| EXPECT_EQ(out.transmit_codec_frame_size_bytes, 8u); |
| EXPECT_EQ(out.receive_codec_frame_size_bytes, 8u); |
| |
| EXPECT_EQ(out.input_bandwidth, 32000u); |
| EXPECT_EQ(out.output_bandwidth, 32000u); |
| |
| EXPECT_EQ(out.input_coding_format.coding_format, bt::hci::CodingFormat::kLinearPcm); |
| EXPECT_EQ(out.input_coding_format.company_id, 0u); |
| EXPECT_EQ(out.input_coding_format.vendor_codec_id, 0u); |
| |
| EXPECT_EQ(out.output_coding_format.coding_format, bt::hci::CodingFormat::kLinearPcm); |
| EXPECT_EQ(out.output_coding_format.company_id, 0u); |
| EXPECT_EQ(out.output_coding_format.vendor_codec_id, 0u); |
| |
| EXPECT_EQ(out.input_coded_data_size_bits, 16u); |
| EXPECT_EQ(out.output_coded_data_size_bits, 16u); |
| |
| EXPECT_EQ(out.input_pcm_data_format, bt::hci::PcmDataFormat::k2sComplement); |
| EXPECT_EQ(out.output_pcm_data_format, bt::hci::PcmDataFormat::k2sComplement); |
| |
| EXPECT_EQ(out.input_pcm_sample_payload_msb_position, 3u); |
| EXPECT_EQ(out.output_pcm_sample_payload_msb_position, 3u); |
| |
| EXPECT_EQ(out.input_data_path, static_cast<bt::hci::ScoDataPath>(6)); |
| EXPECT_EQ(out.output_data_path, static_cast<bt::hci::ScoDataPath>(6)); |
| |
| EXPECT_EQ(out.input_transport_unit_size_bits, 0u); |
| EXPECT_EQ(out.output_transport_unit_size_bits, 0u); |
| |
| EXPECT_EQ(out.max_latency_ms, bt::sco::kParameterSetMsbcT2.max_latency_ms); |
| EXPECT_EQ(out.packet_types, bt::sco::kParameterSetMsbcT2.packet_types); |
| EXPECT_EQ(out.retransmission_effort, bt::sco::kParameterSetMsbcT2.retransmission_effort); |
| |
| // When the IO coding format is Linear PCM, the PCM data format is required. |
| params.clear_io_pcm_data_format(); |
| EXPECT_TRUE(FidlToScoParameters(params).is_error()); |
| |
| // PCM_FLOAT is not a supported PCM format. |
| params.set_io_pcm_data_format(faudio::SampleFormat::PCM_FLOAT); |
| EXPECT_TRUE(FidlToScoParameters(params).is_error()); |
| |
| // PCM format for non-PCM IO coding formats is kNotApplicable and MSB is 0. |
| params.set_io_coding_format(fbredr::CodingFormat::TRANSPARENT); |
| ASSERT_TRUE(FidlToScoParameters(params).is_ok()); |
| out = FidlToScoParameters(params).value(); |
| EXPECT_EQ(out.input_pcm_data_format, bt::hci::PcmDataFormat::kNotApplicable); |
| EXPECT_EQ(out.input_pcm_sample_payload_msb_position, 0u); |
| } |
| |
| } // namespace |
| } // namespace bthost::fidl_helpers |