blob: 5d7f8de80b1e736ae97d3694d95d1a452485fe98 [file] [log] [blame]
// Copyright 2018 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 "garnet/drivers/bluetooth/lib/sdp/service_record.h"
#include "garnet/drivers/bluetooth/lib/sdp/data_element.h"
#include "gtest/gtest.h"
#include "garnet/drivers/bluetooth/lib/common/byte_buffer.h"
#include "garnet/drivers/bluetooth/lib/common/test_helpers.h"
namespace btlib {
namespace sdp {
namespace {
using SDP_ServiceRecordTest = ::testing::Test;
// Test: making a new record generates a UUID.
// Test: GetAttribute
// Test: HasAttribute
// - returns true if attribute is there
// - returns false if attribute is not there
// Test: RemoveAttribute
// Test: SetServiceClassUUIDs
// - Sets the right attribute with the right format.
TEST_F(SDP_ServiceRecordTest, BasicFunctionality) {
ServiceRecord record;
record.SetHandle(kSDPHandle);
EXPECT_EQ(kSDPHandle, record.handle());
EXPECT_TRUE(record.HasAttribute(kServiceId));
EXPECT_FALSE(record.HasAttribute(kServiceClassIdList));
// This isn't a valid service class ID list:
// - ServiceDiscoveryServerServiceClassID
// - BrowseGroupDesciptorServiceClassID
common::UUID sdp_id(uint16_t(0x1000));
common::UUID group_id(uint16_t(0x1001));
std::vector<common::UUID> service_class;
service_class.push_back(sdp_id);
service_class.emplace_back(group_id);
record.SetServiceClassUUIDs(service_class);
EXPECT_TRUE(record.HasAttribute(kServiceClassIdList));
const DataElement& elem = record.GetAttribute(kServiceClassIdList);
EXPECT_EQ(DataElement::Type::kSequence, elem.type());
std::optional<std::vector<DataElement>> vec =
elem.Get<std::vector<DataElement>>();
EXPECT_TRUE(vec);
EXPECT_EQ(2u, vec->size());
EXPECT_EQ(sdp_id, *(vec->at(0).Get<common::UUID>()));
EXPECT_EQ(group_id, *(vec->at(1).Get<common::UUID>()));
record.RemoveAttribute(kServiceId);
EXPECT_FALSE(record.HasAttribute(kServiceId));
}
// Test: GetAttributesInRange
// - Returns any attributes that are present.
TEST_F(SDP_ServiceRecordTest, GetAttributesInRange) {
ServiceRecord record;
record.SetHandle(kSDPHandle);
record.SetAttribute(0xf00d, DataElement());
record.SetAttribute(0x0001, DataElement());
record.SetAttribute(0xfeed, DataElement());
auto attrs =
record.GetAttributesInRange(kServiceRecordHandle, kServiceRecordHandle);
EXPECT_EQ(1u, attrs.size());
EXPECT_EQ(kServiceRecordHandle, *attrs.begin());
// Get a copy of all elements
attrs = record.GetAttributesInRange(0, 0xFFFF);
EXPECT_EQ(5u, attrs.size()); // kServiceRecord, kServiceId, three added above
EXPECT_NE(attrs.end(), attrs.find(kServiceRecordHandle));
EXPECT_NE(attrs.end(), attrs.find(kServiceId));
EXPECT_NE(attrs.end(), attrs.find(0xf00d));
EXPECT_NE(attrs.end(), attrs.find(0x0001));
EXPECT_NE(attrs.end(), attrs.find(0xfeed));
}
// Test: FindUUID
// - Only returns true if all uuids are present
TEST_F(SDP_ServiceRecordTest, FindUUID) {
ServiceRecord record;
DataElement elem;
elem.Set(common::UUID(uint16_t(0xfeaa)));
record.SetAttribute(0xb001, std::move(elem));
elem.Set(common::UUID(uint16_t(0xfeed)));
record.SetAttribute(0xb002, std::move(elem));
elem.Set(common::UUID(uint16_t(0xfeec)));
record.SetAttribute(0xb003, std::move(elem));
std::unordered_set<common::UUID> search_pattern;
search_pattern.insert(common::UUID(uint16_t(0xfeaa)));
EXPECT_TRUE(record.FindUUID(search_pattern));
search_pattern.insert(common::UUID(uint16_t(0xfeec)));
EXPECT_TRUE(record.FindUUID(search_pattern));
search_pattern.insert(common::UUID(uint16_t(0xfeeb)));
EXPECT_FALSE(record.FindUUID(search_pattern));
}
// Test: AddProtocolDescriptor
TEST_F(SDP_ServiceRecordTest, AddProtocolDescriptor) {
ServiceRecord record;
EXPECT_FALSE(record.HasAttribute(kProtocolDescriptorList));
DataElement psm(uint16_t(0x0001)); // SDP PSM
record.AddProtocolDescriptor(ServiceRecord::kPrimaryProtocolList,
protocol::kL2CAP, std::move(psm));
// clang-format off
auto expected = common::CreateStaticByteBuffer(
0x35, 0x08, // Data Element Sequence (8 bytes)
0x35, 0x06, // Data Element Sequence (6 bytes)
0x19, // UUID (16 bits)
0x01, 0x00, // L2CAP protocol UUID
0x09, // uint16_t
0x00, 0x01 // PSM=SDP
);
// clang-format on
EXPECT_TRUE(record.HasAttribute(kProtocolDescriptorList));
const DataElement& val = record.GetAttribute(kProtocolDescriptorList);
common::DynamicByteBuffer block(val.WriteSize());
val.Write(&block);
EXPECT_EQ(expected.size(), block.size());
EXPECT_TRUE(ContainersEqual(expected, block));
record.AddProtocolDescriptor(ServiceRecord::kPrimaryProtocolList,
protocol::kSDP, DataElement());
EXPECT_TRUE(record.HasAttribute(kProtocolDescriptorList));
// clang-format off
auto expected_sdp = common::CreateStaticByteBuffer(
0x35, 0x0D, // Data Element Sequence (13 bytes)
0x35, 0x06, // Data Element Sequence (6 bytes)
0x19, // UUID (16 bits)
0x01, 0x00, // L2CAP protocol UUID
0x09, // uint16_t
0x00, 0x01, // PSM=SDP
0x35, 0x03, // Data Element Sequence (3 bytes)
0x19, // UUID (16 bits)
0x00, 0x01 // kSDP protocol
);
// clang-format on
const DataElement& pdl = record.GetAttribute(kProtocolDescriptorList);
common::DynamicByteBuffer block_sdp(pdl.WriteSize());
pdl.Write(&block_sdp);
EXPECT_EQ(expected_sdp.size(), block_sdp.size());
EXPECT_TRUE(ContainersEqual(expected_sdp, block_sdp));
record.AddProtocolDescriptor(1, protocol::kRFCOMM, DataElement());
EXPECT_TRUE(record.HasAttribute(kAdditionalProtocolDescriptorList));
// clang-format off
auto expected_addl = common::CreateStaticByteBuffer(
0x35, 0x07, // Data Element Sequence (AdditionalProtocolDescriptorLists)
0x35, 0x05, // Data Element Sequence (ProtocolDescriptorList 1)
0x35, 0x03, // Data Element Sequence Protocol List 1 Descriptor 0
0x19, // UUID (16 bits)
0x00, 0x03 // kRFCOMM protocol
);
// clang-format on
const DataElement& apdl =
record.GetAttribute(kAdditionalProtocolDescriptorList);
common::DynamicByteBuffer block_addl(apdl.WriteSize());
apdl.Write(&block_addl);
EXPECT_EQ(expected_addl.size(), block_addl.size());
EXPECT_TRUE(ContainersEqual(expected_addl, block_addl));
}
// Test: AddProfile
// - Adds an attribute if it doesn't exist
// - Appends to the attribute if it does exist
TEST_F(SDP_ServiceRecordTest, AddProfile) {
ServiceRecord record;
EXPECT_FALSE(record.HasAttribute(kBluetoothProfileDescriptorList));
record.AddProfile(profile::kSerialPort, 2, 3);
EXPECT_TRUE(record.HasAttribute(kBluetoothProfileDescriptorList));
// clang-format off
auto expected = common::CreateStaticByteBuffer(
0x35, 0x08, // Data Element Sequence (8 bytes)
0x35, 0x06, // Data Element Sequence (6 bytes)
0x19, // UUID (16 bits)
0x11, 0x01, // SerialPort protocol UUID
0x09, // uint16_t
0x02, 0x03 // 16 bit profile version number (major=2, minor=3)
);
// clang-format on
const DataElement& val = record.GetAttribute(kBluetoothProfileDescriptorList);
common::DynamicByteBuffer block(val.WriteSize());
val.Write(&block);
EXPECT_EQ(expected.size(), block.size());
EXPECT_TRUE(ContainersEqual(expected, block));
record.AddProfile(profile::kDialupNetworking, 4, 5);
// clang-format off
auto expected_dun = common::CreateStaticByteBuffer(
0x35, 0x10, // Data Element Sequence (16 bytes)
0x35, 0x06, // Data Element Sequence (6 bytes)
0x19, // UUID (16 bits)
0x11, 0x01, // SerialPort protocol UUID
0x09, // uint16_t
0x02, 0x03, // 16 bit profile version number (major=2, minor=3)
0x35, 0x06, // Data Element Sequence (6 bytes)
0x19, // UUID (16 bits)
0x11, 0x03, // DUN UUID
0x09, // uint16_t
0x04, 0x05 // 16 bit profile version number (major=4, minor=5)
);
// clang-format on
const DataElement& val_dun =
record.GetAttribute(kBluetoothProfileDescriptorList);
common::DynamicByteBuffer block_dun(val_dun.WriteSize());
val_dun.Write(&block_dun);
EXPECT_EQ(expected_dun.size(), block_dun.size());
EXPECT_TRUE(ContainersEqual(expected_dun, block_dun));
}
// Test: AddInfo
// - Requires at least one is set.
// - Adds the right attributes to a set.
TEST_F(SDP_ServiceRecordTest, AddInfo) {
ServiceRecord record;
EXPECT_FALSE(record.HasAttribute(kLanguageBaseAttributeIdList));
// Can't add with nothing specified.
EXPECT_FALSE(record.AddInfo("en", "", "", ""));
EXPECT_FALSE(record.HasAttribute(kLanguageBaseAttributeIdList));
EXPECT_TRUE(record.AddInfo("en", "SDP", "💖", ""));
EXPECT_TRUE(record.HasAttribute(kLanguageBaseAttributeIdList));
const DataElement& val = record.GetAttribute(kLanguageBaseAttributeIdList);
auto triplets = val.Get<std::vector<DataElement>>();
EXPECT_TRUE(triplets);
// They have to be triplets in this.
EXPECT_TRUE(triplets->size() % 3 == 0);
EXPECT_EQ(DataElement::Type::kUnsignedInt, triplets->at(0).type());
EXPECT_EQ(DataElement::Type::kUnsignedInt, triplets->at(1).type());
EXPECT_EQ(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(record.HasAttribute(*base_attrid + kServiceNameOffset));
const DataElement& name_elem =
record.GetAttribute(*base_attrid + kServiceNameOffset);
auto name = name_elem.Get<std::string>();
EXPECT_TRUE(name);
EXPECT_EQ("SDP", *name);
EXPECT_TRUE(record.HasAttribute(*base_attrid + kServiceDescriptionOffset));
const DataElement& desc_elem =
record.GetAttribute(*base_attrid + kServiceDescriptionOffset);
auto desc = desc_elem.Get<std::string>();
EXPECT_TRUE(desc);
EXPECT_EQ("💖", *desc);
EXPECT_FALSE(record.HasAttribute(*base_attrid + kProviderNameOffset));
};
} // namespace
} // namespace sdp
} // namespace btlib