| // 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 <pw_async/dispatcher.h> |
| |
| #include "fuchsia/bluetooth/bredr/cpp/fidl.h" |
| #include "fuchsia/bluetooth/cpp/fidl.h" |
| #include "fuchsia/bluetooth/le/cpp/fidl.h" |
| #include "fuchsia/bluetooth/sys/cpp/fidl.h" |
| #include "fuchsia/media/cpp/fidl.h" |
| #include "lib/fidl/cpp/comparison.h" |
| #include "src/connectivity/bluetooth/core/bt-host/fidl/adapter_test_fixture.h" |
| #include "src/connectivity/bluetooth/core/bt-host/fidl/fake_adapter_test_fixture.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/advertising_data.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/device_address.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/uuid.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/gap.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/sco/sco.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/sdp/sdp.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/sm/types.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/loop_fixture.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/test_helpers.h" |
| |
| namespace fble = fuchsia::bluetooth::le; |
| namespace fbt = fuchsia::bluetooth; |
| namespace fsys = fuchsia::bluetooth::sys; |
| namespace fbg = fuchsia::bluetooth::gatt; |
| namespace fbg2 = fuchsia::bluetooth::gatt2; |
| 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, |
| /*secure_connections=*/true); |
| const bt::sm::LTK kTestLtk(kTestSecurity, bt::hci_spec::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, 0b11000011}}; |
| const fbt::Address kRandomAddrResolvableFidl{fbt::AddressType::RANDOM, |
| {0x55, 0x44, 0x33, 0x22, 0x11, 0b01000011}}; |
| const fbt::Address kRandomAddrNonResolvableFidl{fbt::AddressType::RANDOM, |
| {0x55, 0x44, 0x33, 0x22, 0x11, 0x00}}; |
| |
| const bt::DeviceAddress kTestPeerAddr(bt::DeviceAddress::Type::kBREDR, {1, 0, 0, 0, 0, 0}); |
| const bt::DeviceAddress kLePublicAddress(bt::DeviceAddress::Type::kLEPublic, {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}; |
| |
| class HelpersTestWithLoop : public bt::testing::TestLoopFixture { |
| public: |
| pw::async::Dispatcher& pw_dispatcher() { return pw_dispatcher_; } |
| |
| private: |
| pw::async::fuchsia::FuchsiaDispatcher pw_dispatcher_{dispatcher()}; |
| }; |
| |
| TEST(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::kNotReady)); |
| } |
| |
| TEST(HelpersTest, GattErrorToFidl) { |
| // Host errors |
| EXPECT_EQ(fbg::Error::INVALID_RESPONSE, |
| GattErrorToFidl(bt::Error(bt::HostError::kPacketMalformed))); |
| EXPECT_EQ(fbg::Error::FAILURE, GattErrorToFidl(bt::Error(bt::HostError::kTimedOut))); |
| |
| // Protocol errors |
| EXPECT_EQ(fbg::Error::INSUFFICIENT_AUTHORIZATION, |
| GattErrorToFidl(bt::att::Error(bt::att::ErrorCode::kInsufficientAuthorization))); |
| EXPECT_EQ(fbg::Error::INSUFFICIENT_AUTHENTICATION, |
| GattErrorToFidl(bt::att::Error(bt::att::ErrorCode::kInsufficientAuthentication))); |
| EXPECT_EQ(fbg::Error::INSUFFICIENT_ENCRYPTION_KEY_SIZE, |
| GattErrorToFidl(bt::att::Error(bt::att::ErrorCode::kInsufficientEncryptionKeySize))); |
| EXPECT_EQ(fbg::Error::INSUFFICIENT_ENCRYPTION, |
| GattErrorToFidl(bt::att::Error(bt::att::ErrorCode::kInsufficientEncryption))); |
| EXPECT_EQ(fbg::Error::READ_NOT_PERMITTED, |
| GattErrorToFidl(bt::att::Error(bt::att::ErrorCode::kReadNotPermitted))); |
| EXPECT_EQ(fbg::Error::FAILURE, |
| GattErrorToFidl(bt::att::Error(bt::att::ErrorCode::kUnlikelyError))); |
| } |
| |
| TEST(HelpersTest, AttErrorToGattFidlError) { |
| // Host errors |
| EXPECT_EQ(fbg2::Error::INVALID_PDU, |
| AttErrorToGattFidlError(bt::Error(bt::HostError::kPacketMalformed))); |
| EXPECT_EQ(fbg2::Error::INVALID_PARAMETERS, |
| AttErrorToGattFidlError(bt::Error(bt::HostError::kInvalidParameters))); |
| EXPECT_EQ(fbg2::Error::UNLIKELY_ERROR, |
| AttErrorToGattFidlError(bt::Error(bt::HostError::kTimedOut))); |
| |
| // Protocol errors |
| EXPECT_EQ( |
| fbg2::Error::INSUFFICIENT_AUTHORIZATION, |
| AttErrorToGattFidlError(bt::att::Error(bt::att::ErrorCode::kInsufficientAuthorization))); |
| EXPECT_EQ( |
| fbg2::Error::INSUFFICIENT_AUTHENTICATION, |
| AttErrorToGattFidlError(bt::att::Error(bt::att::ErrorCode::kInsufficientAuthentication))); |
| EXPECT_EQ( |
| fbg2::Error::INSUFFICIENT_ENCRYPTION_KEY_SIZE, |
| AttErrorToGattFidlError(bt::att::Error(bt::att::ErrorCode::kInsufficientEncryptionKeySize))); |
| EXPECT_EQ(fbg2::Error::INSUFFICIENT_ENCRYPTION, |
| AttErrorToGattFidlError(bt::att::Error(bt::att::ErrorCode::kInsufficientEncryption))); |
| EXPECT_EQ(fbg2::Error::READ_NOT_PERMITTED, |
| AttErrorToGattFidlError(bt::att::Error(bt::att::ErrorCode::kReadNotPermitted))); |
| EXPECT_EQ(fbg2::Error::INVALID_HANDLE, |
| AttErrorToGattFidlError(bt::att::Error(bt::att::ErrorCode::kInvalidHandle))); |
| EXPECT_EQ(fbg2::Error::UNLIKELY_ERROR, |
| AttErrorToGattFidlError(bt::att::Error(bt::att::ErrorCode::kUnlikelyError))); |
| } |
| |
| TEST(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(HelpersTest, UuidFromFidl) { |
| // Test HLCPP FIDL bindings with fuchsia::bluetooth::Uuid |
| 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 new C++ FIDL bindings with fuchsia_bluetooth::Uuid |
| fuchsia_bluetooth::Uuid input2; |
| input2.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. |
| output = NewUuidFromFidl(input2); |
| EXPECT_EQ("0000180d-0000-1000-8000-00805f9b34fb", output.ToString()); |
| EXPECT_EQ(2u, output.CompactSize()); |
| } |
| |
| TEST(HelpersTest, FidlToScmsTEnableTest) { |
| bt::StaticPacket<android_emb::A2dpScmsTEnableWriter> result_enable = FidlToScmsTEnable(true); |
| EXPECT_EQ(result_enable.view().enabled().Read(), |
| pw::bluetooth::emboss::GenericEnableParam::ENABLE); |
| EXPECT_EQ(result_enable.view().header().Read(), 0x0); |
| |
| bt::StaticPacket<android_emb::A2dpScmsTEnableWriter> result_disable = FidlToScmsTEnable(false); |
| EXPECT_EQ(result_disable.view().enabled().Read(), |
| pw::bluetooth::emboss::GenericEnableParam::DISABLE); |
| EXPECT_EQ(result_disable.view().header().Read(), 0x0); |
| } |
| |
| TEST(HelpersTest, FidlToEncoderSettingsSbcTest) { |
| std::unique_ptr<fbredr::AudioEncoderSettings> encoder_settings = |
| std::make_unique<fbredr::AudioEncoderSettings>(); |
| std::unique_ptr<fuchsia::media::SbcEncoderSettings> value = |
| fuchsia::media::SbcEncoderSettings::New(); |
| encoder_settings->set_sbc(*value); |
| |
| fbredr::AudioSamplingFrequency sampling_frequency = fbredr::AudioSamplingFrequency::HZ_44100; |
| fbredr::AudioChannelMode channel_mode = fbredr::AudioChannelMode::MONO; |
| |
| bt::StaticPacket<android_emb::SbcCodecInformationWriter> result = |
| FidlToEncoderSettingsSbc(*encoder_settings, sampling_frequency, channel_mode); |
| |
| EXPECT_EQ(android_emb::SbcAllocationMethod::LOUDNESS, result.view().allocation_method().Read()); |
| EXPECT_EQ(android_emb::SbcSubBands::SUBBANDS_8, result.view().subbands().Read()); |
| EXPECT_EQ(android_emb::SbcBlockLen::BLOCK_LEN_4, result.view().block_length().Read()); |
| EXPECT_EQ(0, result.view().min_bitpool_value().Read()); |
| EXPECT_EQ(0, result.view().max_bitpool_value().Read()); |
| EXPECT_EQ(android_emb::SbcSamplingFrequency::HZ_44100, result.view().sampling_frequency().Read()); |
| EXPECT_EQ(android_emb::SbcChannelMode::MONO, result.view().channel_mode().Read()); |
| } |
| |
| TEST(HelpersTest, FidlToEncoderSettingsAacTest) { |
| std::unique_ptr<fbredr::AudioEncoderSettings> encoder_settings = |
| std::make_unique<fbredr::AudioEncoderSettings>(); |
| std::unique_ptr<fuchsia::media::AacEncoderSettings> value = |
| fuchsia::media::AacEncoderSettings::New(); |
| value->aot = fuchsia::media::AacAudioObjectType::MPEG4_AAC_LC; |
| value->bit_rate.set_variable(::fuchsia::media::AacVariableBitRate::V1); |
| encoder_settings->set_aac(std::move(*value)); |
| |
| fbredr::AudioSamplingFrequency sampling_frequency = fbredr::AudioSamplingFrequency::HZ_44100; |
| fbredr::AudioChannelMode channel_mode = fbredr::AudioChannelMode::MONO; |
| |
| bt::StaticPacket<android_emb::AacCodecInformationWriter> result = |
| FidlToEncoderSettingsAac(*encoder_settings, sampling_frequency, channel_mode); |
| |
| EXPECT_EQ(result.view().object_type().Read(), 1); |
| EXPECT_EQ(result.view().variable_bit_rate().Read(), |
| android_emb::AacEnableVariableBitRate::ENABLE); |
| } |
| |
| template <typename T> |
| void FidlToDataElementIntegerTest(const std::function<fbredr::DataElement(T&&)>& func, |
| bt::sdp::DataElement::Type type) { |
| fbredr::DataElement data_element = func(std::numeric_limits<T>::max()); |
| std::optional<bt::sdp::DataElement> result = FidlToDataElement(data_element); |
| ASSERT_TRUE(result.has_value()); |
| EXPECT_EQ(type, result->type()); |
| EXPECT_EQ(std::numeric_limits<T>::max(), result->Get<T>()); |
| |
| // result->size() returns an enum member of DataElement::Size indicating the number of bytes |
| uint8_t exponent = static_cast<uint8_t>(result->size()); |
| EXPECT_EQ(sizeof(T), std::pow(2, exponent)); |
| } |
| |
| TEST(HelpersTest, FidlToDataElementInt8Test) { |
| FidlToDataElementIntegerTest(std::function(fbredr::DataElement::WithInt8), |
| bt::sdp::DataElement::Type::kSignedInt); |
| } |
| |
| TEST(HelpersTest, FidlToDataElementInt16Test) { |
| FidlToDataElementIntegerTest(std::function(fbredr::DataElement::WithInt16), |
| bt::sdp::DataElement::Type::kSignedInt); |
| } |
| |
| TEST(HelpersTest, FidlToDataElementInt32Test) { |
| FidlToDataElementIntegerTest(std::function(fbredr::DataElement::WithInt32), |
| bt::sdp::DataElement::Type::kSignedInt); |
| } |
| |
| TEST(HelpersTest, FidlToDataElementInt64Test) { |
| FidlToDataElementIntegerTest(std::function(fbredr::DataElement::WithInt64), |
| bt::sdp::DataElement::Type::kSignedInt); |
| } |
| |
| TEST(HelpersTest, FidlToDataElementUint8Test) { |
| FidlToDataElementIntegerTest(std::function(fbredr::DataElement::WithUint8), |
| bt::sdp::DataElement::Type::kUnsignedInt); |
| } |
| |
| TEST(HelpersTest, FidlToDataElementUint16Test) { |
| FidlToDataElementIntegerTest(std::function(fbredr::DataElement::WithUint16), |
| bt::sdp::DataElement::Type::kUnsignedInt); |
| } |
| |
| TEST(HelpersTest, FidlToDataElementUint32Test) { |
| FidlToDataElementIntegerTest(std::function(fbredr::DataElement::WithUint32), |
| bt::sdp::DataElement::Type::kUnsignedInt); |
| } |
| |
| TEST(HelpersTest, FidlToDataElementUint64Test) { |
| FidlToDataElementIntegerTest(std::function(fbredr::DataElement::WithUint64), |
| bt::sdp::DataElement::Type::kUnsignedInt); |
| } |
| |
| TEST(HelpersTest, FidlToDataElementEmptyStringTest) { |
| std::vector<uint8_t> data; |
| ASSERT_EQ(0u, data.size()); |
| |
| fbredr::DataElement data_element = fbredr::DataElement::WithStr(std::move(data)); |
| std::optional<bt::sdp::DataElement> result = FidlToDataElement(data_element); |
| |
| EXPECT_TRUE(result.has_value()); |
| EXPECT_EQ(bt::sdp::DataElement::Type::kString, result->type()); |
| EXPECT_EQ("", result->Get<std::string>()); |
| EXPECT_EQ(bt::sdp::DataElement::Size::kNextOne, result->size()); |
| } |
| |
| TEST(HelpersTest, FidlToDataElementStringTest) { |
| std::string expected_str = "foobarbaz"; |
| std::vector<uint8_t> data(expected_str.size(), 0); |
| std::memcpy(data.data(), expected_str.data(), expected_str.size()); |
| |
| fbredr::DataElement data_element = fbredr::DataElement::WithStr(std::move(data)); |
| std::optional<bt::sdp::DataElement> result = FidlToDataElement(data_element); |
| |
| EXPECT_TRUE(result.has_value()); |
| EXPECT_EQ(bt::sdp::DataElement::Type::kString, result->type()); |
| EXPECT_EQ(expected_str, result->Get<std::string>()); |
| EXPECT_EQ(bt::sdp::DataElement::Size::kNextOne, result->size()); |
| } |
| |
| TEST(HelpersTest, FidlToDataElementUrlTest) { |
| std::string url = "http://www.google.com"; |
| std::string moved = url; |
| fbredr::DataElement data_element = fbredr::DataElement::WithUrl(std::move(moved)); |
| std::optional<bt::sdp::DataElement> result = FidlToDataElement(data_element); |
| |
| EXPECT_TRUE(result.has_value()); |
| EXPECT_EQ(bt::sdp::DataElement::Type::kUrl, result->type()); |
| EXPECT_EQ(url, result->GetUrl()); |
| EXPECT_EQ(bt::sdp::DataElement::Size::kNextOne, result->size()); |
| } |
| |
| TEST(HelpersTest, FidlToDataElementBooleanTest) { |
| bool expected = true; |
| bool moved = expected; |
| fbredr::DataElement data_element = fbredr::DataElement::WithB(std::move(moved)); |
| std::optional<bt::sdp::DataElement> result = FidlToDataElement(data_element); |
| |
| EXPECT_TRUE(result.has_value()); |
| EXPECT_EQ(bt::sdp::DataElement::Type::kBoolean, result->type()); |
| EXPECT_EQ(expected, result->Get<bool>()); |
| EXPECT_EQ(bt::sdp::DataElement::Size::kOneByte, result->size()); |
| } |
| |
| TEST(HelpersTest, FidlToDataElementUuidTest) { |
| std::unique_ptr<fbt::Uuid> uuid = fbt::Uuid::New(); |
| uuid->value.fill(123); |
| |
| fbredr::DataElement data_element = fbredr::DataElement::WithUuid(std::move(*uuid)); |
| std::optional<bt::sdp::DataElement> result = FidlToDataElement(data_element); |
| |
| ASSERT_TRUE(result.has_value()); |
| EXPECT_EQ(bt::sdp::DataElement::Type::kUuid, result->type()); |
| EXPECT_EQ(bt::sdp::DataElement::Size::kSixteenBytes, result->size()); |
| |
| bt::DynamicByteBuffer bytes(16); |
| bytes.Fill(123); |
| |
| bt::UUID expected; |
| ASSERT_TRUE(bt::UUID::FromBytes(bytes, &expected)); |
| EXPECT_EQ(expected, result->Get<bt::UUID>()); |
| } |
| |
| TEST(HelpersTest, FidlToDataElementSequenceTest) { |
| int8_t size = 3; |
| std::vector<std::unique_ptr<fbredr::DataElement>> moved; |
| std::vector<bt::sdp::DataElement> expected; |
| |
| for (int16_t i = 0; i < size; i++) { |
| expected.emplace_back(i); |
| moved.push_back( |
| std::make_unique<fbredr::DataElement>(fbredr::DataElement::WithInt16(std::move(i)))); |
| } |
| |
| fbredr::DataElement data_element = fbredr::DataElement::WithSequence(std::move(moved)); |
| std::optional<bt::sdp::DataElement> result = FidlToDataElement(data_element); |
| |
| EXPECT_TRUE(result.has_value()); |
| EXPECT_EQ(bt::sdp::DataElement::Type::kSequence, result->type()); |
| EXPECT_EQ(bt::sdp::DataElement::Size::kNextOne, result->size()); |
| |
| std::optional<std::vector<bt::sdp::DataElement>> actual = |
| result->Get<std::vector<bt::sdp::DataElement>>(); |
| EXPECT_TRUE(actual); |
| |
| for (int8_t i = 0; i < size; i++) { |
| EXPECT_EQ(expected[i].Get<int16_t>(), actual.value()[i].Get<int16_t>()); |
| } |
| } |
| |
| template <typename T> |
| void NewFidlToDataElementIntegerTest( |
| const std::function<fuchsia_bluetooth_bredr::DataElement(T)>& func, |
| bt::sdp::DataElement::Type type) { |
| fuchsia_bluetooth_bredr::DataElement data_element = func(std::numeric_limits<T>::max()); |
| std::optional<bt::sdp::DataElement> result = NewFidlToDataElement(data_element); |
| ASSERT_TRUE(result.has_value()); |
| EXPECT_EQ(type, result->type()); |
| EXPECT_EQ(std::numeric_limits<T>::max(), result->Get<T>()); |
| |
| // result->size() returns an enum member of DataElement::Size indicating the number of bytes |
| uint8_t exponent = static_cast<uint8_t>(result->size()); |
| EXPECT_EQ(sizeof(T), std::pow(2, exponent)); |
| } |
| |
| TEST(HelpersTest, NewFidlToDataElementInt8Test) { |
| NewFidlToDataElementIntegerTest(std::function(fuchsia_bluetooth_bredr::DataElement::WithInt8), |
| bt::sdp::DataElement::Type::kSignedInt); |
| } |
| |
| TEST(HelpersTest, NewFidlToDataElementInt16Test) { |
| NewFidlToDataElementIntegerTest(std::function(fuchsia_bluetooth_bredr::DataElement::WithInt16), |
| bt::sdp::DataElement::Type::kSignedInt); |
| } |
| |
| TEST(HelpersTest, NewFidlToDataElementInt32Test) { |
| NewFidlToDataElementIntegerTest(std::function(fuchsia_bluetooth_bredr::DataElement::WithInt32), |
| bt::sdp::DataElement::Type::kSignedInt); |
| } |
| |
| TEST(HelpersTest, NewFidlToDataElementInt64Test) { |
| NewFidlToDataElementIntegerTest(std::function(fuchsia_bluetooth_bredr::DataElement::WithInt64), |
| bt::sdp::DataElement::Type::kSignedInt); |
| } |
| |
| TEST(HelpersTest, NewFidlToDataElementUint8Test) { |
| NewFidlToDataElementIntegerTest(std::function(fuchsia_bluetooth_bredr::DataElement::WithUint8), |
| bt::sdp::DataElement::Type::kUnsignedInt); |
| } |
| |
| TEST(HelpersTest, NewFidlToDataElementUint16Test) { |
| NewFidlToDataElementIntegerTest(std::function(fuchsia_bluetooth_bredr::DataElement::WithUint16), |
| bt::sdp::DataElement::Type::kUnsignedInt); |
| } |
| |
| TEST(HelpersTest, NewFidlToDataElementUint32Test) { |
| NewFidlToDataElementIntegerTest(std::function(fuchsia_bluetooth_bredr::DataElement::WithUint32), |
| bt::sdp::DataElement::Type::kUnsignedInt); |
| } |
| |
| TEST(HelpersTest, NewFidlToDataElementUint64Test) { |
| NewFidlToDataElementIntegerTest(std::function(fuchsia_bluetooth_bredr::DataElement::WithUint64), |
| bt::sdp::DataElement::Type::kUnsignedInt); |
| } |
| |
| TEST(HelpersTest, NewFidlToDataElementEmptyStringTest) { |
| std::vector<uint8_t> data; |
| ASSERT_EQ(0u, data.size()); |
| |
| fuchsia_bluetooth_bredr::DataElement data_element = |
| fuchsia_bluetooth_bredr::DataElement::WithStr(std::move(data)); |
| std::optional<bt::sdp::DataElement> result = NewFidlToDataElement(data_element); |
| |
| EXPECT_TRUE(result.has_value()); |
| EXPECT_EQ(bt::sdp::DataElement::Type::kString, result->type()); |
| EXPECT_EQ("", result->Get<std::string>()); |
| EXPECT_EQ(bt::sdp::DataElement::Size::kNextOne, result->size()); |
| } |
| |
| TEST(HelpersTest, NewFidlToDataElementStringTest) { |
| std::string expected_str = "foobarbaz"; |
| std::vector<uint8_t> data(expected_str.size(), 0); |
| std::memcpy(data.data(), expected_str.data(), expected_str.size()); |
| |
| fuchsia_bluetooth_bredr::DataElement data_element = |
| fuchsia_bluetooth_bredr::DataElement::WithStr(std::move(data)); |
| std::optional<bt::sdp::DataElement> result = NewFidlToDataElement(data_element); |
| |
| EXPECT_TRUE(result.has_value()); |
| EXPECT_EQ(bt::sdp::DataElement::Type::kString, result->type()); |
| EXPECT_EQ(expected_str, result->Get<std::string>()); |
| EXPECT_EQ(bt::sdp::DataElement::Size::kNextOne, result->size()); |
| } |
| |
| TEST(HelpersTest, NewFidlToDataElementUrlTest) { |
| std::string url = "http://www.google.com"; |
| std::string moved = url; |
| fuchsia_bluetooth_bredr::DataElement data_element = |
| fuchsia_bluetooth_bredr::DataElement::WithUrl(std::move(moved)); |
| std::optional<bt::sdp::DataElement> result = NewFidlToDataElement(data_element); |
| |
| EXPECT_TRUE(result.has_value()); |
| EXPECT_EQ(bt::sdp::DataElement::Type::kUrl, result->type()); |
| EXPECT_EQ(url, result->GetUrl()); |
| EXPECT_EQ(bt::sdp::DataElement::Size::kNextOne, result->size()); |
| } |
| |
| TEST(HelpersTest, NewFidlToDataElementBooleanTest) { |
| bool expected = true; |
| bool moved = expected; |
| fuchsia_bluetooth_bredr::DataElement data_element = |
| fuchsia_bluetooth_bredr::DataElement::WithB(std::move(moved)); |
| std::optional<bt::sdp::DataElement> result = NewFidlToDataElement(data_element); |
| |
| EXPECT_TRUE(result.has_value()); |
| EXPECT_EQ(bt::sdp::DataElement::Type::kBoolean, result->type()); |
| EXPECT_EQ(expected, result->Get<bool>()); |
| EXPECT_EQ(bt::sdp::DataElement::Size::kOneByte, result->size()); |
| } |
| |
| TEST(HelpersTest, NewFidlToDataElementUuidTest) { |
| fuchsia_bluetooth::Uuid uuid; |
| uuid.value().fill(123); |
| |
| fuchsia_bluetooth_bredr::DataElement data_element = |
| fuchsia_bluetooth_bredr::DataElement::WithUuid(std::move(uuid)); |
| std::optional<bt::sdp::DataElement> result = NewFidlToDataElement(data_element); |
| |
| ASSERT_TRUE(result.has_value()); |
| EXPECT_EQ(bt::sdp::DataElement::Type::kUuid, result->type()); |
| EXPECT_EQ(bt::sdp::DataElement::Size::kSixteenBytes, result->size()); |
| |
| bt::DynamicByteBuffer bytes(16); |
| bytes.Fill(123); |
| |
| bt::UUID expected; |
| ASSERT_TRUE(bt::UUID::FromBytes(bytes, &expected)); |
| EXPECT_EQ(expected, result->Get<bt::UUID>()); |
| } |
| |
| TEST(HelpersTest, NewFidlToDataElementSequenceTest) { |
| int8_t size = 3; |
| std::vector<fidl::Box<::fuchsia_bluetooth_bredr::DataElement>> moved; |
| std::vector<bt::sdp::DataElement> expected; |
| |
| for (int16_t i = 0; i < size; i++) { |
| expected.emplace_back(i); |
| moved.push_back(std::make_unique<fuchsia_bluetooth_bredr::DataElement>( |
| fuchsia_bluetooth_bredr::DataElement::WithInt16(std::move(i)))); |
| } |
| |
| fuchsia_bluetooth_bredr::DataElement data_element = |
| fuchsia_bluetooth_bredr::DataElement::WithSequence(std::move(moved)); |
| std::optional<bt::sdp::DataElement> result = NewFidlToDataElement(data_element); |
| |
| EXPECT_TRUE(result.has_value()); |
| EXPECT_EQ(bt::sdp::DataElement::Type::kSequence, result->type()); |
| EXPECT_EQ(bt::sdp::DataElement::Size::kNextOne, result->size()); |
| |
| std::optional<std::vector<bt::sdp::DataElement>> actual = |
| result->Get<std::vector<bt::sdp::DataElement>>(); |
| EXPECT_TRUE(actual); |
| |
| for (int8_t i = 0; i < size; i++) { |
| EXPECT_EQ(expected[i].Get<int16_t>(), actual.value()[i].Get<int16_t>()); |
| } |
| } |
| |
| TEST(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(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()->name); |
| } |
| |
| TEST(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(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(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(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(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(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(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(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); |
| bt::StaticByteBuffer service_bytes(0x01, 0x02); |
| EXPECT_TRUE(input.AddServiceUuid(test_uuid)); |
| EXPECT_TRUE(input.SetServiceData(test_uuid, service_bytes.view())); |
| |
| uint16_t company_id = 0x98; |
| bt::StaticByteBuffer manufacturer_bytes(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 = std::make_unique<fbt::Int8>(); |
| expected_power_level->value = 4; |
| EXPECT_EQ(expected_power_level->value, output.tx_power_level->value); |
| |
| auto expected_appearance = std::make_unique<fbt::UInt16>(); |
| 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(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(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); |
| bt::StaticByteBuffer service_bytes(0x01, 0x02); |
| EXPECT_TRUE(input.AddServiceUuid(test_uuid)); |
| EXPECT_TRUE(input.SetServiceData(test_uuid, service_bytes.view())); |
| |
| uint16_t company_id = 0x98; |
| bt::StaticByteBuffer manufacturer_bytes(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 = std::make_unique<fbt::Int8>(); |
| 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(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(HelpersTest, BrEdrSecurityModeFromFidl) { |
| EXPECT_EQ(bt::gap::BrEdrSecurityMode::Mode4, |
| BrEdrSecurityModeFromFidl(fsys::BrEdrSecurityMode::MODE_4)); |
| EXPECT_EQ(bt::gap::BrEdrSecurityMode::SecureConnectionsOnly, |
| BrEdrSecurityModeFromFidl(fsys::BrEdrSecurityMode::SECURE_CONNECTIONS_ONLY)); |
| auto nonexistent_security_mode = static_cast<fsys::BrEdrSecurityMode>(0xFF); |
| EXPECT_EQ(std::nullopt, BrEdrSecurityModeFromFidl(nonexistent_security_mode)); |
| } |
| |
| TEST(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(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(HelpersTest, SecurityLevelFromFidl) { |
| const fsys::PairingSecurityLevel level = fsys::PairingSecurityLevel::AUTHENTICATED; |
| EXPECT_EQ(bt::sm::SecurityLevel::kAuthenticated, SecurityLevelFromFidl(level)); |
| } |
| |
| TEST(HelpersTest, SecurityLevelFromBadFidlFails) { |
| int nonexistant_security_level = 500000; |
| auto level = static_cast<fsys::PairingSecurityLevel>(nonexistant_security_level); |
| EXPECT_EQ(std::nullopt, SecurityLevelFromFidl(level)); |
| } |
| |
| TEST(HelpersTest, PeerToFidlMandatoryFields) { |
| // Required by PeerCache expiry functions. |
| async::TestLoop test_loop; |
| pw::async::fuchsia::FuchsiaDispatcher pw_dispatcher(test_loop.dispatcher()); |
| |
| bt::gap::PeerCache cache(pw_dispatcher); |
| 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(HelpersTest, PeerToFidlOptionalFields) { |
| // Required by PeerCache expiry functions. |
| async::TestLoop test_loop; |
| pw::async::fuchsia::FuchsiaDispatcher pw_dispatcher(test_loop.dispatcher()); |
| |
| const int8_t kRssi = 5; |
| const int8_t kTxPower = 6; |
| const auto kAdv = |
| bt::StaticByteBuffer(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})}; |
| |
| bt::gap::PeerCache cache(pw_dispatcher); |
| 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, pw::chrono::SystemClock::time_point()); |
| bt::StaticPacket<pw::bluetooth::emboss::InquiryResultWriter> inquiry_result; |
| auto view = inquiry_result.view(); |
| view.bd_addr().CopyFrom(bt::DeviceAddressBytes{{0, 1, 2, 3, 4, 5}}.view()); |
| view.page_scan_repetition_mode().Write(pw::bluetooth::emboss::PageScanRepetitionMode::R0_); |
| view.class_of_device().major_device_class().Write( |
| pw::bluetooth::emboss::MajorDeviceClass::PERIPHERAL); |
| peer->MutBrEdr().SetInquiryData(inquiry_result.view()); |
| 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 https://fxbug.dev/42135180). |
| EXPECT_FALSE(fidl.has_services()); |
| |
| // TODO(https://fxbug.dev/42135180): 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(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(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 a URL attribute. |
| bt::sdp::AttributeId url_attr_id = 0x1112; // Random ID |
| fuchsia::bluetooth::bredr::Attribute url_attribute; |
| url_attribute.element.set_url("foobar.dev"); |
| url_attribute.id = url_attr_id; |
| def.mutable_additional_attributes()->emplace_back(std::move(url_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::StaticByteBuffer(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::StaticByteBuffer(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_TRUE(rec.value().HasAttribute(url_attr_id)); |
| EXPECT_EQ(*rec.value().GetAttribute(url_attr_id).GetUrl(), "foobar.dev"); |
| } |
| |
| TEST(HelpersTest, ObexServiceDefinitionToServiceRecord) { |
| // Create an OBEX definition for a successful conversion. MAP MSE uses both L2CAP and RFCOMM. |
| fuchsia::bluetooth::bredr::ServiceDefinition def; |
| def.mutable_service_class_uuids()->emplace_back( |
| fidl_helpers::UuidToFidl(bt::sdp::profile::kMessageAccessServer)); |
| |
| // The primary protocol contains L2CAP, RFCOMM, & OBEX. |
| fuchsia::bluetooth::bredr::ProtocolDescriptor l2cap_proto; |
| l2cap_proto.protocol = fuchsia::bluetooth::bredr::ProtocolIdentifier::L2CAP; |
| def.mutable_protocol_descriptor_list()->emplace_back(std::move(l2cap_proto)); |
| |
| fuchsia::bluetooth::bredr::ProtocolDescriptor rfcomm_proto; |
| rfcomm_proto.protocol = fuchsia::bluetooth::bredr::ProtocolIdentifier::RFCOMM; |
| fuchsia::bluetooth::bredr::DataElement rfcomm_data_el; |
| rfcomm_data_el.set_uint8(10); // Random ServerChannel number |
| rfcomm_proto.params.emplace_back(std::move(rfcomm_data_el)); |
| def.mutable_protocol_descriptor_list()->emplace_back(std::move(rfcomm_proto)); |
| |
| fuchsia::bluetooth::bredr::ProtocolDescriptor obex_proto; |
| obex_proto.protocol = fuchsia::bluetooth::bredr::ProtocolIdentifier::OBEX; |
| def.mutable_protocol_descriptor_list()->emplace_back(std::move(obex_proto)); |
| |
| // Profile version = 1.4. |
| fuchsia::bluetooth::bredr::ProfileDescriptor prof_desc; |
| prof_desc.profile_id = |
| fuchsia::bluetooth::bredr::ServiceClassProfileIdentifier::MESSAGE_ACCESS_PROFILE; |
| prof_desc.major_version = 1; |
| prof_desc.minor_version = 4; |
| def.mutable_profile_descriptors()->emplace_back(prof_desc); |
| |
| // ServiceName = "MAP MAS" |
| fuchsia::bluetooth::bredr::Information info; |
| info.set_language("en"); |
| info.set_name("MAP MAS"); |
| def.mutable_information()->emplace_back(std::move(info)); |
| |
| // GoepL2capPsm Attribute with a dynamic PSM. |
| fuchsia::bluetooth::bredr::Attribute goep_l2cap_psm_attribute; |
| goep_l2cap_psm_attribute.id = bt::sdp::kGoepL2capPsm; |
| goep_l2cap_psm_attribute.element.set_uint16(fuchsia::bluetooth::bredr::PSM_DYNAMIC); |
| def.mutable_additional_attributes()->emplace_back(std::move(goep_l2cap_psm_attribute)); |
| |
| // Add MASInstanceID Attribute = 1 |
| fuchsia::bluetooth::bredr::Attribute instance_id_attribute; |
| instance_id_attribute.id = 0x0315; |
| instance_id_attribute.element.set_uint16(1); |
| def.mutable_additional_attributes()->emplace_back(std::move(instance_id_attribute)); |
| // Add SupportedMessagesTypes Attribute = Email (0) |
| fuchsia::bluetooth::bredr::Attribute message_types_attribute; |
| message_types_attribute.id = 0x0316; |
| message_types_attribute.element.set_uint16(0); |
| def.mutable_additional_attributes()->emplace_back(std::move(message_types_attribute)); |
| // Add MapSupportedFeatures Attribute = Notification Registration only (1) |
| fuchsia::bluetooth::bredr::Attribute features_attribute; |
| features_attribute.id = 0x0317; |
| features_attribute.element.set_uint16(1); |
| def.mutable_additional_attributes()->emplace_back(std::move(features_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::kMessageAccessServer}; |
| 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("MAP MAS", *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::StaticByteBuffer(0x35, 0x11, // Data Element Sequence (11 bytes) |
| 0x35, 0x03, // Data Element Sequence (3 bytes) |
| 0x19, // UUID (16 bits) |
| 0x01, 0x00, // L2CAP Profile UUID |
| 0x35, 0x05, // Data Element Sequence (5 bytes) |
| 0x19, // UUID |
| 0x00, 0x03, // RFCOMM Profile UUID |
| 0x08, // uint8_t |
| 0x0a, // ServerChannel = 10 |
| 0x35, 0x03, // Data Element Sequence (3 bytes) |
| 0x19, // UUID |
| 0x00, 0x08 // OBEX Profile UUID |
| ); |
| 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::StaticByteBuffer(0x35, 0x08, // Data Element Sequence (8 bytes) |
| 0x35, 0x06, // Data Element Sequence (6 bytes) |
| 0x19, // UUID |
| 0x11, 0x34, // Message Access Profile ID |
| 0x09, // uint16_t |
| 0x01, 0x04 // 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(0x0315)); |
| EXPECT_TRUE(rec.value().HasAttribute(0x0316)); |
| EXPECT_TRUE(rec.value().HasAttribute(0x0317)); |
| // The GoepL2capPsm value should be saved. |
| EXPECT_EQ(*rec.value().GetAttribute(bt::sdp::kGoepL2capPsm).Get<uint16_t>(), |
| fuchsia::bluetooth::bredr::PSM_DYNAMIC); |
| } |
| |
| TEST(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(HelpersTest, AddressFromFidlBondingDataRandomAddressRejectedIfBredr) { |
| fsys::BondingData bond; |
| bond.set_address(kRandomAddrFidl); |
| bond.set_bredr_bond(fsys::BredrBondData()); |
| |
| EXPECT_EQ(std::nullopt, AddressFromFidlBondingData(bond)); |
| } |
| |
| TEST(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(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(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(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(HelpersTest, AddressFromFidlBondingDataLeRandomResolvable) { |
| fsys::BondingData bond; |
| bond.set_address(kRandomAddrResolvableFidl); |
| bond.set_le_bond(fsys::LeBondData()); |
| |
| auto addr = AddressFromFidlBondingData(bond); |
| EXPECT_FALSE(addr); |
| } |
| |
| TEST(HelpersTest, AddressFromFidlBondingDataLeRandomNonResolvable) { |
| fsys::BondingData bond; |
| bond.set_address(kRandomAddrNonResolvableFidl); |
| bond.set_le_bond(fsys::LeBondData()); |
| |
| auto addr = AddressFromFidlBondingData(bond); |
| EXPECT_FALSE(addr); |
| } |
| |
| TEST(HelpersTest, LePairingDataFromFidlEmpty) { |
| bt::sm::PairingData result = LePairingDataFromFidl(kLePublicAddress, 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(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(kLePublicAddress, std::move(le)); |
| ASSERT_TRUE(result.local_ltk); |
| ASSERT_TRUE(result.peer_ltk); |
| ASSERT_TRUE(result.irk); |
| ASSERT_TRUE(result.identity_address); |
| ASSERT_TRUE(result.csrk); |
| |
| EXPECT_EQ(kTestLtk, *result.local_ltk); |
| EXPECT_EQ(kTestLtk, *result.peer_ltk); |
| EXPECT_EQ(kTestKey, *result.irk); |
| EXPECT_EQ(kLePublicAddress, *result.identity_address); |
| EXPECT_EQ(kTestKey, *result.csrk); |
| } |
| |
| TEST(HelpersTest, BredrKeyFromFidlEmpty) { EXPECT_FALSE(BredrKeyFromFidl(fsys::BredrBondData())); } |
| |
| TEST(HelpersTest, BredrKeyFromFidl) { |
| const bt::sm::SecurityProperties kTestSecurity(bt::sm::SecurityLevel::kSecureAuthenticated, 16, |
| /*secure_connections=*/true); |
| const bt::sm::LTK kTestLtk(kTestSecurity, bt::hci_spec::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(HelpersTest, BredrServicesFromFidlEmpty) { |
| EXPECT_TRUE(BredrServicesFromFidl(fsys::BredrBondData()).empty()); |
| } |
| |
| TEST(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 HelpersAdapterTest : public bthost::testing::AdapterTestFixture {}; |
| |
| TEST_F(HelpersAdapterTest, PeerToFidlBondingData_NoTransportData) { |
| auto* peer = adapter()->peer_cache()->NewPeer(kTestPeerAddr, /*connectable=*/true); |
| fsys::BondingData data = PeerToFidlBondingData(adapter().get(), *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(HelpersAdapterTest, PeerToFidlBondingData_BothTransportsPresentButNotBonded) { |
| auto* peer = adapter()->peer_cache()->NewPeer(kTestPeerAddr, /*connectable=*/true); |
| peer->MutLe(); |
| peer->MutBrEdr(); |
| |
| fsys::BondingData data = PeerToFidlBondingData(adapter().get(), *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(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().get(), *peer); |
| EXPECT_FALSE(data.has_bredr_bond()); |
| } |
| |
| TEST_F(HelpersAdapterTest, PeerToFidlBondingData_EmptyLeData) { |
| auto* peer = adapter()->peer_cache()->NewPeer(kTestPeerAddr, /*connectable=*/true); |
| peer->MutLe().SetBondData(bt::sm::PairingData()); |
| |
| fsys::BondingData data = PeerToFidlBondingData(adapter().get(), *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(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().get(), *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(HelpersAdapterTest, PeerToFidlBondingData_BredrData) { |
| auto* peer = adapter()->peer_cache()->NewPeer(kTestPeerAddr, /*connectable=*/true); |
| peer->MutBrEdr().SetBondData(kTestLtk); |
| |
| fsys::BondingData data = PeerToFidlBondingData(adapter().get(), *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(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().get(), *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))); |
| } |
| |
| // Returns a copy to avoid forming references to misaligned fields, which is UB. |
| template <typename T> |
| T copy(T t) { |
| return t; |
| } |
| |
| TEST_F(HelpersAdapterTest, FidlToScoParameters) { |
| fbredr::ScoConnectionParameters params; |
| EXPECT_TRUE(FidlToScoParameters(params).is_error()); |
| params.set_parameter_set(fbredr::HfpParameterSet::T2); |
| EXPECT_TRUE(FidlToScoParameters(params).is_error()); |
| params.set_air_coding_format(fbt::AssignedCodingFormat::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(fbt::AssignedCodingFormat::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::StaticPacket<pw::bluetooth::emboss::SynchronousConnectionParametersWriter> out = |
| FidlToScoParameters(params).take_value(); |
| auto view = out.view(); |
| EXPECT_EQ(view.transmit_bandwidth().Read(), 8000u); |
| EXPECT_EQ(view.receive_bandwidth().Read(), 8000u); |
| |
| EXPECT_EQ(view.transmit_coding_format().coding_format().Read(), |
| pw::bluetooth::emboss::CodingFormat::MSBC); |
| EXPECT_EQ(view.transmit_coding_format().company_id().Read(), 0u); |
| EXPECT_EQ(view.transmit_coding_format().vendor_codec_id().Read(), 0u); |
| |
| EXPECT_EQ(view.receive_coding_format().coding_format().Read(), |
| pw::bluetooth::emboss::CodingFormat::MSBC); |
| EXPECT_EQ(view.receive_coding_format().company_id().Read(), 0u); |
| EXPECT_EQ(view.receive_coding_format().vendor_codec_id().Read(), 0u); |
| |
| EXPECT_EQ(view.transmit_codec_frame_size_bytes().Read(), 8u); |
| EXPECT_EQ(view.receive_codec_frame_size_bytes().Read(), 8u); |
| |
| EXPECT_EQ(view.input_bandwidth().Read(), 32000u); |
| EXPECT_EQ(view.output_bandwidth().Read(), 32000u); |
| |
| EXPECT_EQ(view.input_coding_format().coding_format().Read(), |
| pw::bluetooth::emboss::CodingFormat::LINEAR_PCM); |
| EXPECT_EQ(view.input_coding_format().company_id().Read(), 0u); |
| EXPECT_EQ(view.input_coding_format().vendor_codec_id().Read(), 0u); |
| |
| EXPECT_EQ(view.output_coding_format().coding_format().Read(), |
| pw::bluetooth::emboss::CodingFormat::LINEAR_PCM); |
| EXPECT_EQ(view.output_coding_format().company_id().Read(), 0u); |
| EXPECT_EQ(view.output_coding_format().vendor_codec_id().Read(), 0u); |
| |
| EXPECT_EQ(view.input_coded_data_size_bits().Read(), 16u); |
| EXPECT_EQ(view.output_coded_data_size_bits().Read(), 16u); |
| |
| EXPECT_EQ(view.input_pcm_data_format().Read(), |
| pw::bluetooth::emboss::PcmDataFormat::TWOS_COMPLEMENT); |
| EXPECT_EQ(view.output_pcm_data_format().Read(), |
| pw::bluetooth::emboss::PcmDataFormat::TWOS_COMPLEMENT); |
| |
| EXPECT_EQ(view.input_pcm_sample_payload_msb_position().Read(), 3u); |
| EXPECT_EQ(view.output_pcm_sample_payload_msb_position().Read(), 3u); |
| |
| EXPECT_EQ(view.input_data_path().Read(), static_cast<pw::bluetooth::emboss::ScoDataPath>(6)); |
| EXPECT_EQ(view.output_data_path().Read(), static_cast<pw::bluetooth::emboss::ScoDataPath>(6)); |
| |
| EXPECT_EQ(view.input_transport_unit_size_bits().Read(), 0u); |
| EXPECT_EQ(view.output_transport_unit_size_bits().Read(), 0u); |
| |
| EXPECT_EQ(view.max_latency_ms().Read(), bt::sco::kParameterSetT2.max_latency_ms); |
| EXPECT_EQ(view.packet_types().BackingStorage().ReadUInt(), bt::sco::kParameterSetT2.packet_types); |
| EXPECT_EQ( |
| view.retransmission_effort().Read(), |
| static_cast<pw::bluetooth::emboss::SynchronousConnectionParameters::ScoRetransmissionEffort>( |
| bt::sco::kParameterSetT2.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(fbt::AssignedCodingFormat::TRANSPARENT); |
| ASSERT_TRUE(FidlToScoParameters(params).is_ok()); |
| out = FidlToScoParameters(params).value(); |
| EXPECT_EQ(view.input_pcm_data_format().Read(), |
| pw::bluetooth::emboss::PcmDataFormat::NOT_APPLICABLE); |
| EXPECT_EQ(view.input_pcm_sample_payload_msb_position().Read(), 0u); |
| } |
| |
| class HelpersTestFakeAdapter : public bt::fidl::testing::FakeAdapterTestFixture { |
| public: |
| HelpersTestFakeAdapter() = default; |
| ~HelpersTestFakeAdapter() override = default; |
| |
| void SetUp() override { FakeAdapterTestFixture::SetUp(); } |
| |
| void TearDown() override { FakeAdapterTestFixture::TearDown(); } |
| |
| private: |
| BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(HelpersTestFakeAdapter); |
| }; |
| |
| TEST_F(HelpersTestFakeAdapter, 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_local_name()); |
| ASSERT_TRUE(host_info.has_discoverable()); |
| ASSERT_TRUE(host_info.has_discovering()); |
| ASSERT_TRUE(host_info.has_addresses()); |
| |
| EXPECT_EQ(adapter()->identifier().value(), host_info.id().value); |
| EXPECT_EQ(fsys::TechnologyType::DUAL_MODE, host_info.technology()); |
| EXPECT_EQ("", host_info.local_name()); |
| EXPECT_TRUE(host_info.discoverable()); |
| EXPECT_TRUE(host_info.discovering()); |
| // Since privacy is not enabled by default, only the public address should be reported. |
| ASSERT_EQ(host_info.addresses().size(), 1u); |
| EXPECT_EQ(fbt::AddressType::PUBLIC, host_info.addresses()[0].type); |
| EXPECT_TRUE(ContainersEqual(adapter()->state().controller_address.bytes(), |
| host_info.addresses()[0].bytes)); |
| } |
| |
| TEST_F(HelpersTestFakeAdapter, HostInfoWithLEAddressToFidl) { |
| // Enabling privacy does not result in the LE random address being set immediately. Therefore, |
| // only the public address should be reported. |
| adapter()->fake_le()->EnablePrivacy(/*enabled=*/true); |
| auto host_info = HostInfoToFidl(*adapter()); |
| EXPECT_EQ(adapter()->identifier().value(), host_info.id().value); |
| EXPECT_EQ(fsys::TechnologyType::DUAL_MODE, host_info.technology()); |
| EXPECT_EQ("", host_info.local_name()); |
| EXPECT_TRUE(host_info.discoverable()); |
| EXPECT_TRUE(host_info.discovering()); |
| ASSERT_EQ(host_info.addresses().size(), 1u); |
| EXPECT_EQ(fbt::AddressType::PUBLIC, host_info.addresses()[0].type); |
| EXPECT_TRUE(ContainersEqual(adapter()->state().controller_address.bytes(), |
| host_info.addresses()[0].bytes)); |
| |
| // Setting a RPA should result in the LE random address being reported. |
| auto resolvable_address = |
| bt::DeviceAddress(bt::DeviceAddress::Type::kLERandom, {0x55, 0x44, 0x33, 0x22, 0x11, 0x43}); |
| adapter()->fake_le()->UpdateRandomAddress(resolvable_address); |
| host_info = HostInfoToFidl(*adapter()); |
| ASSERT_EQ(host_info.addresses().size(), 2u); |
| EXPECT_EQ(fbt::AddressType::PUBLIC, host_info.addresses()[0].type); |
| EXPECT_TRUE(ContainersEqual(adapter()->state().controller_address.bytes(), |
| host_info.addresses()[0].bytes)); |
| EXPECT_EQ(fbt::AddressType::RANDOM, host_info.addresses()[1].type); |
| EXPECT_TRUE(ContainersEqual(adapter()->le()->CurrentAddress().value().bytes(), |
| host_info.addresses()[1].bytes)); |
| |
| // Setting a static address should result in the LE random address being reported. |
| auto static_address = |
| bt::DeviceAddress(bt::DeviceAddress::Type::kLERandom, {0x55, 0x44, 0x33, 0x22, 0x11, 0x43}); |
| adapter()->fake_le()->UpdateRandomAddress(static_address); |
| host_info = HostInfoToFidl(*adapter()); |
| ASSERT_EQ(host_info.addresses().size(), 2u); |
| EXPECT_EQ(fbt::AddressType::RANDOM, host_info.addresses()[1].type); |
| EXPECT_TRUE(ContainersEqual(adapter()->le()->CurrentAddress().value().bytes(), |
| host_info.addresses()[1].bytes)); |
| } |
| |
| TEST(HelpersTest, DiscoveryFilterFromEmptyFidlFilter) { |
| bt::gap::DiscoveryFilter filter = DiscoveryFilterFromFidl(fble::Filter()); |
| EXPECT_TRUE(filter.service_uuids().empty()); |
| EXPECT_TRUE(filter.service_data_uuids().empty()); |
| EXPECT_FALSE(filter.manufacturer_code().has_value()); |
| EXPECT_FALSE(filter.connectable().has_value()); |
| EXPECT_TRUE(filter.name_substring().empty()); |
| EXPECT_FALSE(filter.pathloss().has_value()); |
| } |
| |
| TEST(HelpersTest, DiscoveryFilterFromFidlFilter) { |
| fble::Filter fidl_filter; |
| fuchsia::bluetooth::Uuid service_uuid; |
| service_uuid.value[0] = 1; |
| fuchsia::bluetooth::Uuid service_data_uuid; |
| service_uuid.value[0] = 2; |
| fidl_filter.set_service_uuid(service_uuid); |
| fidl_filter.set_service_data_uuid(service_data_uuid); |
| fidl_filter.set_manufacturer_id(2); |
| fidl_filter.set_connectable(true); |
| fidl_filter.set_name("name"); |
| fidl_filter.set_max_path_loss(3); |
| bt::gap::DiscoveryFilter filter = DiscoveryFilterFromFidl(fidl_filter); |
| EXPECT_THAT(filter.service_uuids(), ::testing::ElementsAre(service_uuid.value)); |
| EXPECT_THAT(filter.service_data_uuids(), ::testing::ElementsAre(service_data_uuid.value)); |
| ASSERT_TRUE(filter.manufacturer_code().has_value()); |
| EXPECT_EQ(filter.manufacturer_code().value(), 2u); |
| ASSERT_TRUE(filter.connectable().has_value()); |
| EXPECT_EQ(filter.connectable().value(), true); |
| EXPECT_EQ(filter.name_substring(), "name"); |
| ASSERT_TRUE(filter.pathloss().has_value()); |
| ASSERT_EQ(filter.pathloss().value(), 3); |
| } |
| |
| TEST(HelpersTest, EmptyAdvertisingDataToFidlScanData) { |
| bt::AdvertisingData input; |
| fble::ScanData output = AdvertisingDataToFidlScanData( |
| input, pw::chrono::SystemClock::time_point(std::chrono::nanoseconds(1))); |
| EXPECT_FALSE(output.has_tx_power()); |
| 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()); |
| ASSERT_TRUE(output.has_timestamp()); |
| EXPECT_EQ(output.timestamp(), zx::time(1).get()); |
| } |
| |
| TEST(HelpersTest, AdvertisingDataToFidlScanData) { |
| bt::AdvertisingData input; |
| input.SetTxPower(4); |
| const uint16_t kAppearance = 193u; // WATCH_SPORTS |
| input.SetAppearance(kAppearance); |
| |
| const uint16_t id = 0x5678; |
| const bt::UUID kServiceUuid = bt::UUID(id); |
| auto service_bytes = bt::StaticByteBuffer(0x01, 0x02); |
| EXPECT_TRUE(input.AddServiceUuid(kServiceUuid)); |
| EXPECT_TRUE(input.SetServiceData(kServiceUuid, service_bytes.view())); |
| |
| const uint16_t kManufacturer = 0x98; |
| auto manufacturer_bytes = bt::StaticByteBuffer(0x04, 0x03); |
| EXPECT_TRUE(input.SetManufacturerData(kManufacturer, manufacturer_bytes.view())); |
| |
| const char* const kUri = "http://fuchsia.cl/461435"; |
| EXPECT_TRUE(input.AddUri(kUri)); |
| |
| fble::ScanData output = AdvertisingDataToFidlScanData( |
| input, pw::chrono::SystemClock::time_point(std::chrono::nanoseconds(1))); |
| EXPECT_EQ(4, output.tx_power()); |
| EXPECT_EQ(fbt::Appearance{kAppearance}, output.appearance()); |
| ASSERT_EQ(1u, output.service_uuids().size()); |
| EXPECT_EQ(kServiceUuid, UuidFromFidl(output.service_uuids().front())); |
| ASSERT_EQ(1u, output.service_data().size()); |
| ASSERT_TRUE(output.has_timestamp()); |
| EXPECT_EQ(output.timestamp(), zx::time(1).get()); |
| auto service_data = output.service_data().front(); |
| EXPECT_EQ(kServiceUuid, 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(kManufacturer, manufacturer_data.company_id); |
| EXPECT_TRUE(ContainersEqual(bt::BufferView(manufacturer_bytes), manufacturer_data.data)); |
| EXPECT_THAT(output.uris(), ::testing::ElementsAre(kUri)); |
| } |
| |
| TEST(HelpersTest, AdvertisingDataToFidlScanDataOmitsNonEnumeratedAppearance) { |
| // 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( |
| AdvertisingDataToFidlScanData(input, pw::chrono::SystemClock::time_point()).has_appearance()); |
| |
| const uint16_t kKnownAppearance = 832u; // HEART_RATE_SENSOR |
| input.SetAppearance(kKnownAppearance); |
| |
| EXPECT_TRUE( |
| AdvertisingDataToFidlScanData(input, pw::chrono::SystemClock::time_point()).has_appearance()); |
| } |
| |
| TEST_F(HelpersTestWithLoop, PeerToFidlLeWithAllFields) { |
| RunLoopFor(zx::duration(2)); |
| |
| const bt::PeerId kPeerId(1); |
| const bt::DeviceAddress kAddr(bt::DeviceAddress::Type::kLEPublic, {1, 0, 0, 0, 0, 0}); |
| bt::gap::PeerMetrics metrics; |
| bt::gap::Peer peer([](auto&, auto) {}, [](auto&) {}, [](auto&) {}, [](auto&) { return false; }, |
| kPeerId, kAddr, |
| /*connectable=*/true, &metrics, pw_dispatcher()); |
| peer.RegisterName("name"); |
| const int8_t kRssi = 1; |
| auto adv_bytes = bt::StaticByteBuffer( |
| // Uri: "https://abc.xyz" |
| 0x0B, bt::DataType::kURI, 0x17, '/', '/', 'a', 'b', 'c', '.', 'x', 'y', 'z'); |
| peer.MutLe().SetAdvertisingData(kRssi, adv_bytes, |
| pw::chrono::SystemClock::time_point(std::chrono::nanoseconds(1))); |
| |
| fble::Peer fidl_peer = PeerToFidlLe(peer); |
| ASSERT_TRUE(fidl_peer.has_id()); |
| EXPECT_EQ(fidl_peer.id().value, kPeerId.value()); |
| ASSERT_TRUE(fidl_peer.has_bonded()); |
| EXPECT_FALSE(fidl_peer.bonded()); |
| ASSERT_TRUE(fidl_peer.has_name()); |
| EXPECT_EQ(fidl_peer.name(), "name"); |
| ASSERT_TRUE(fidl_peer.has_rssi()); |
| EXPECT_EQ(fidl_peer.rssi(), kRssi); |
| ASSERT_TRUE(fidl_peer.has_data()); |
| EXPECT_THAT(fidl_peer.data().uris(), ::testing::ElementsAre("https://abc.xyz")); |
| ASSERT_TRUE(fidl_peer.has_last_updated()); |
| EXPECT_EQ(fidl_peer.last_updated(), 2); |
| ASSERT_TRUE(fidl_peer.data().has_timestamp()); |
| EXPECT_EQ(fidl_peer.data().timestamp(), 1); |
| } |
| |
| TEST_F(HelpersTestWithLoop, PeerToFidlLeWithoutAdvertisingData) { |
| const bt::PeerId kPeerId(1); |
| const bt::DeviceAddress kAddr(bt::DeviceAddress::Type::kLEPublic, {1, 0, 0, 0, 0, 0}); |
| bt::gap::PeerMetrics metrics; |
| bt::gap::Peer peer([](auto&, auto) {}, [](auto&) {}, [](auto&) {}, [](auto&) { return false; }, |
| kPeerId, kAddr, |
| /*connectable=*/true, &metrics, pw_dispatcher()); |
| |
| fble::Peer fidl_peer = PeerToFidlLe(peer); |
| ASSERT_TRUE(fidl_peer.has_id()); |
| EXPECT_EQ(fidl_peer.id().value, kPeerId.value()); |
| ASSERT_TRUE(fidl_peer.has_bonded()); |
| EXPECT_FALSE(fidl_peer.bonded()); |
| EXPECT_FALSE(fidl_peer.has_name()); |
| EXPECT_FALSE(fidl_peer.has_rssi()); |
| EXPECT_FALSE(fidl_peer.has_data()); |
| ASSERT_TRUE(fidl_peer.has_last_updated()); |
| EXPECT_EQ(fidl_peer.last_updated(), 0); |
| } |
| |
| TEST(HelpersTest, PeerIdFromString) { |
| EXPECT_FALSE(PeerIdFromString(std::string())); |
| EXPECT_FALSE(PeerIdFromString("-1")); |
| EXPECT_FALSE(PeerIdFromString("g")); |
| EXPECT_EQ(PeerIdFromString("0"), std::optional(bt::PeerId(0))); |
| EXPECT_EQ(PeerIdFromString("FFFFFFFFFFFFFFFF"), |
| std::optional(bt::PeerId(std::numeric_limits<uint64_t>::max()))); |
| // ID is 1 more than max. |
| EXPECT_FALSE(PeerIdFromString("10000000000000000")); |
| EXPECT_EQ(PeerIdFromString("0123456789"), std::optional(bt::PeerId(0x123456789))); |
| EXPECT_EQ(PeerIdFromString("abcdef"), std::optional(bt::PeerId(0xabcdef))); |
| EXPECT_EQ(PeerIdFromString("ABCDEF"), std::optional(bt::PeerId(0xabcdef))); |
| } |
| |
| TEST(HelpersTest, ScoPacketStatusToFidl) { |
| EXPECT_EQ( |
| ScoPacketStatusToFidl(bt::hci_spec::SynchronousDataPacketStatusFlag::kCorrectlyReceived), |
| fuchsia::bluetooth::bredr::RxPacketStatus::CORRECTLY_RECEIVED_DATA); |
| EXPECT_EQ(ScoPacketStatusToFidl(bt::hci_spec::SynchronousDataPacketStatusFlag::kPossiblyInvalid), |
| fuchsia::bluetooth::bredr::RxPacketStatus::POSSIBLY_INVALID_DATA); |
| EXPECT_EQ(ScoPacketStatusToFidl(bt::hci_spec::SynchronousDataPacketStatusFlag::kNoDataReceived), |
| fuchsia::bluetooth::bredr::RxPacketStatus::NO_DATA_RECEIVED); |
| EXPECT_EQ( |
| ScoPacketStatusToFidl(bt::hci_spec::SynchronousDataPacketStatusFlag::kDataPartiallyLost), |
| fuchsia::bluetooth::bredr::RxPacketStatus::DATA_PARTIALLY_LOST); |
| } |
| |
| TEST(HelpersTest, Gatt2DescriptorFromFidlNoPermissions) { |
| fbg2::Descriptor desc; |
| fbt::Uuid fidl_uuid{{}}; |
| desc.set_type(fidl_uuid); |
| desc.set_handle(fbg2::Handle{3}); |
| EXPECT_EQ(Gatt2DescriptorFromFidl(desc), nullptr); |
| } |
| |
| TEST(HelpersTest, Gatt2DescriptorFromFidlNoType) { |
| fbg2::Descriptor desc; |
| fbg2::AttributePermissions permissions; |
| desc.set_permissions(std::move(permissions)); |
| desc.set_handle(fbg2::Handle{3}); |
| EXPECT_EQ(Gatt2DescriptorFromFidl(desc), nullptr); |
| } |
| |
| TEST(HelpersTest, Gatt2DescriptorFromFidlNoHandle) { |
| fbg2::Descriptor desc; |
| fbg2::AttributePermissions permissions; |
| desc.set_permissions(std::move(permissions)); |
| fbt::Uuid fidl_uuid{{}}; |
| desc.set_type(fidl_uuid); |
| EXPECT_EQ(Gatt2DescriptorFromFidl(desc), nullptr); |
| } |
| |
| TEST(HelpersTest, Gatt2DescriptorFromFidlSuccessWithEmptyPermissions) { |
| bt::UInt128 type = {2}; |
| |
| fbg2::Descriptor desc; |
| fbg2::AttributePermissions permissions; |
| desc.set_permissions(std::move(permissions)); |
| fbt::Uuid fidl_uuid{type}; |
| desc.set_type(fidl_uuid); |
| desc.set_handle(fbg2::Handle{3}); |
| |
| std::unique_ptr<bt::gatt::Descriptor> out = Gatt2DescriptorFromFidl(desc); |
| ASSERT_TRUE(out); |
| EXPECT_EQ(out->id(), 3u); |
| EXPECT_EQ(out->type(), bt::UUID(type)); |
| EXPECT_FALSE(out->read_permissions().allowed()); |
| EXPECT_FALSE(out->write_permissions().allowed()); |
| } |
| |
| TEST(HelpersTest, Gatt2DescriptorFromFidlSuccessWithEmptyReadWritePermissions) { |
| fbg2::Descriptor desc; |
| fbg2::AttributePermissions permissions; |
| permissions.set_read(fbg2::SecurityRequirements()); |
| permissions.set_write(fbg2::SecurityRequirements()); |
| desc.set_permissions(std::move(permissions)); |
| fbt::Uuid fidl_uuid{{}}; |
| desc.set_type(fidl_uuid); |
| desc.set_handle(fbg2::Handle{3}); |
| |
| std::unique_ptr<bt::gatt::Descriptor> out = Gatt2DescriptorFromFidl(desc); |
| ASSERT_TRUE(out); |
| EXPECT_TRUE(out->read_permissions().allowed_without_security()); |
| EXPECT_TRUE(out->write_permissions().allowed_without_security()); |
| } |
| |
| TEST(HelpersTest, Gatt2DescriptorFromFidlSuccessWithReadPermissions) { |
| fbg2::Descriptor desc; |
| fbg2::AttributePermissions permissions; |
| fbg2::SecurityRequirements read_reqs; |
| read_reqs.set_authentication_required(true); |
| read_reqs.set_authorization_required(true); |
| read_reqs.set_encryption_required(true); |
| permissions.set_read(std::move(read_reqs)); |
| desc.set_permissions(std::move(permissions)); |
| desc.set_type({{}}); |
| desc.set_handle(fbg2::Handle{3}); |
| |
| std::unique_ptr<bt::gatt::Descriptor> out = Gatt2DescriptorFromFidl(desc); |
| ASSERT_TRUE(out); |
| EXPECT_TRUE(out->read_permissions().encryption_required()); |
| EXPECT_TRUE(out->read_permissions().authentication_required()); |
| EXPECT_TRUE(out->read_permissions().authorization_required()); |
| EXPECT_FALSE(out->write_permissions().allowed()); |
| } |
| |
| TEST(HelpersTest, Gatt2DescriptorFromFidlSuccessWithWritePermissions) { |
| fbg2::Descriptor desc; |
| fbg2::AttributePermissions permissions; |
| fbg2::SecurityRequirements write_reqs; |
| write_reqs.set_authentication_required(true); |
| write_reqs.set_authorization_required(true); |
| write_reqs.set_encryption_required(true); |
| permissions.set_write(std::move(write_reqs)); |
| desc.set_permissions(std::move(permissions)); |
| desc.set_type({{}}); |
| desc.set_handle(fbg2::Handle{3}); |
| |
| std::unique_ptr<bt::gatt::Descriptor> out = Gatt2DescriptorFromFidl(desc); |
| ASSERT_TRUE(out); |
| EXPECT_FALSE(out->read_permissions().allowed()); |
| EXPECT_TRUE(out->write_permissions().encryption_required()); |
| EXPECT_TRUE(out->write_permissions().authentication_required()); |
| EXPECT_TRUE(out->write_permissions().authorization_required()); |
| } |
| |
| TEST(HelpersTest, Gatt2CharacteristicFromFidlNoProperties) { |
| fbg2::Characteristic chrc; |
| chrc.set_permissions(fbg2::AttributePermissions()); |
| chrc.set_type(fbt::Uuid{{}}); |
| chrc.set_handle(fbg2::Handle{3}); |
| EXPECT_FALSE(Gatt2CharacteristicFromFidl(chrc)); |
| } |
| |
| TEST(HelpersTest, Gatt2CharacteristicFromFidlNoPermissions) { |
| fbg2::Characteristic chrc; |
| chrc.mutable_properties(); |
| chrc.set_type(fbt::Uuid{{}}); |
| chrc.set_handle(fbg2::Handle{3}); |
| EXPECT_FALSE(Gatt2CharacteristicFromFidl(chrc)); |
| } |
| |
| TEST(HelpersTest, Gatt2CharacteristicFromFidlSuccessWithPropertiesAndEmptyPermissions) { |
| fbg2::Characteristic chrc; |
| chrc.set_properties(fbg2::CharacteristicPropertyBits::WRITE | |
| fbg2::CharacteristicPropertyBits::RELIABLE_WRITE); |
| chrc.set_permissions(fbg2::AttributePermissions()); |
| bt::UInt128 type = {2}; |
| chrc.set_type(fbt::Uuid{type}); |
| chrc.set_handle(fbg2::Handle{3}); |
| std::unique_ptr<bt::gatt::Characteristic> out = Gatt2CharacteristicFromFidl(chrc); |
| ASSERT_TRUE(out); |
| EXPECT_EQ(out->properties(), |
| bt::gatt::Property::kWrite | bt::gatt::Property::kExtendedProperties); |
| EXPECT_EQ(out->extended_properties(), bt::gatt::ExtendedProperty::kReliableWrite); |
| EXPECT_FALSE(out->write_permissions().allowed()); |
| EXPECT_FALSE(out->read_permissions().allowed()); |
| EXPECT_FALSE(out->update_permissions().allowed()); |
| EXPECT_EQ(out->type(), bt::UUID(type)); |
| EXPECT_EQ(out->id(), 3u); |
| } |
| |
| TEST(HelpersTest, Gatt2CharacteristicFromFidlSupportsNotifyButDoesNotHavePermission) { |
| fbg2::Characteristic chrc; |
| chrc.set_properties(fbg2::CharacteristicPropertyBits::NOTIFY); |
| chrc.set_permissions(fbg2::AttributePermissions()); |
| bt::UInt128 type = {2}; |
| chrc.set_type(fbt::Uuid{type}); |
| chrc.set_handle(fbg2::Handle{3}); |
| std::unique_ptr<bt::gatt::Characteristic> out = Gatt2CharacteristicFromFidl(chrc); |
| ASSERT_FALSE(out); |
| } |
| |
| TEST(HelpersTest, Gatt2CharacteristicFromFidlDoesNotSupportNotifyButDoesHaveUpdatePermission) { |
| fbg2::Characteristic chrc; |
| chrc.mutable_properties(); |
| fbg2::AttributePermissions permissions; |
| permissions.set_update(fbg2::SecurityRequirements()); |
| chrc.set_permissions(std::move(permissions)); |
| bt::UInt128 type = {2}; |
| chrc.set_type(fbt::Uuid{type}); |
| chrc.set_handle(fbg2::Handle{3}); |
| std::unique_ptr<bt::gatt::Characteristic> out = Gatt2CharacteristicFromFidl(chrc); |
| ASSERT_FALSE(out); |
| } |
| |
| TEST(HelpersTest, Gatt2CharacteristicFromFidlSuccessWithEmptySecurityRequirements) { |
| fbg2::Characteristic chrc; |
| chrc.set_properties(fbg2::CharacteristicPropertyBits::NOTIFY); |
| fbg2::AttributePermissions permissions; |
| permissions.set_read(fbg2::SecurityRequirements()); |
| permissions.set_write(fbg2::SecurityRequirements()); |
| permissions.set_update(fbg2::SecurityRequirements()); |
| chrc.set_permissions(std::move(permissions)); |
| bt::UInt128 type = {2}; |
| chrc.set_type(fbt::Uuid{type}); |
| chrc.set_handle(fbg2::Handle{3}); |
| std::unique_ptr<bt::gatt::Characteristic> out = Gatt2CharacteristicFromFidl(chrc); |
| ASSERT_TRUE(out); |
| EXPECT_TRUE(out->write_permissions().allowed_without_security()); |
| EXPECT_TRUE(out->read_permissions().allowed_without_security()); |
| EXPECT_TRUE(out->update_permissions().allowed_without_security()); |
| } |
| |
| TEST(HelpersTest, Gatt2CharacteristicFromFidlSuccessWithAllSecurityRequirements) { |
| fbg2::Characteristic chrc; |
| chrc.set_properties(fbg2::CharacteristicPropertyBits::NOTIFY); |
| fbg2::AttributePermissions permissions; |
| fbg2::SecurityRequirements read_reqs; |
| read_reqs.set_authentication_required(true); |
| read_reqs.set_authorization_required(true); |
| read_reqs.set_encryption_required(true); |
| permissions.set_read(std::move(read_reqs)); |
| fbg2::SecurityRequirements write_reqs; |
| write_reqs.set_authentication_required(true); |
| write_reqs.set_authorization_required(true); |
| write_reqs.set_encryption_required(true); |
| permissions.set_write(std::move(write_reqs)); |
| fbg2::SecurityRequirements update_reqs; |
| update_reqs.set_authentication_required(true); |
| update_reqs.set_authorization_required(true); |
| update_reqs.set_encryption_required(true); |
| permissions.set_update(std::move(update_reqs)); |
| chrc.set_permissions(std::move(permissions)); |
| bt::UInt128 type = {2}; |
| chrc.set_type(fbt::Uuid{type}); |
| chrc.set_handle(fbg2::Handle{3}); |
| std::unique_ptr<bt::gatt::Characteristic> out = Gatt2CharacteristicFromFidl(chrc); |
| ASSERT_TRUE(out); |
| EXPECT_TRUE(out->write_permissions().authentication_required()); |
| EXPECT_TRUE(out->write_permissions().authorization_required()); |
| EXPECT_TRUE(out->write_permissions().encryption_required()); |
| EXPECT_TRUE(out->read_permissions().authentication_required()); |
| EXPECT_TRUE(out->read_permissions().authorization_required()); |
| EXPECT_TRUE(out->read_permissions().encryption_required()); |
| EXPECT_TRUE(out->update_permissions().authentication_required()); |
| EXPECT_TRUE(out->update_permissions().authorization_required()); |
| EXPECT_TRUE(out->update_permissions().encryption_required()); |
| } |
| |
| TEST(HelpersTest, Gatt2CharacteristicFromFidlWithInvalidDescriptor) { |
| fbg2::Characteristic chrc; |
| chrc.mutable_properties(); |
| chrc.set_permissions(fbg2::AttributePermissions()); |
| bt::UInt128 type = {2}; |
| chrc.set_type(fbt::Uuid{type}); |
| chrc.set_handle(fbg2::Handle{3}); |
| std::vector<fbg2::Descriptor> descriptors; |
| descriptors.emplace_back(fbg2::Descriptor()); |
| chrc.set_descriptors(std::move(descriptors)); |
| std::unique_ptr<bt::gatt::Characteristic> out = Gatt2CharacteristicFromFidl(chrc); |
| ASSERT_FALSE(out); |
| } |
| |
| TEST(HelpersTest, Gatt2CharacteristicFromFidlWith2Descriptors) { |
| fbg2::Characteristic chrc; |
| chrc.mutable_properties(); |
| chrc.set_permissions(fbg2::AttributePermissions()); |
| bt::UInt128 chrc_type = {2}; |
| chrc.set_type(fbt::Uuid{chrc_type}); |
| chrc.set_handle(fbg2::Handle{3}); |
| |
| std::vector<fbg2::Descriptor> descriptors; |
| |
| fbg2::Descriptor desc_0; |
| desc_0.set_permissions(fbg2::AttributePermissions()); |
| bt::UInt128 desc_type_0 = {4}; |
| desc_0.set_type(fbt::Uuid{desc_type_0}); |
| desc_0.set_handle(fbg2::Handle{5}); |
| descriptors.push_back(std::move(desc_0)); |
| |
| fbg2::Descriptor desc_1; |
| desc_1.set_permissions(fbg2::AttributePermissions()); |
| bt::UInt128 desc_type_1 = {6}; |
| desc_1.set_type(fbt::Uuid{desc_type_1}); |
| desc_1.set_handle(fbg2::Handle{7}); |
| descriptors.push_back(std::move(desc_1)); |
| |
| chrc.set_descriptors(std::move(descriptors)); |
| |
| std::unique_ptr<bt::gatt::Characteristic> out = Gatt2CharacteristicFromFidl(chrc); |
| ASSERT_TRUE(out); |
| ASSERT_EQ(out->descriptors().size(), 2u); |
| EXPECT_EQ(out->descriptors()[0]->id(), 5u); |
| EXPECT_EQ(out->descriptors()[0]->type(), bt::UUID(desc_type_0)); |
| EXPECT_FALSE(out->descriptors()[0]->read_permissions().allowed()); |
| EXPECT_FALSE(out->descriptors()[0]->write_permissions().allowed()); |
| EXPECT_EQ(out->descriptors()[1]->id(), 7u); |
| EXPECT_EQ(out->descriptors()[1]->type(), bt::UUID(desc_type_1)); |
| EXPECT_FALSE(out->descriptors()[1]->read_permissions().allowed()); |
| EXPECT_FALSE(out->descriptors()[1]->write_permissions().allowed()); |
| } |
| |
| } // namespace |
| } // namespace bthost::fidl_helpers |