blob: 34223dcd67a1e6b1a8a41ebd883043ec5d4fa7e2 [file] [log] [blame]
// Copyright 2017 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 "src/connectivity/bluetooth/core/bt-host/gap/advertising_data.h"
#include "gtest/gtest.h"
#include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h"
#include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h"
namespace bt {
namespace gap {
namespace {
constexpr uint16_t kGattUuid = 0x1801;
constexpr uint16_t kEddystoneUuid = 0xFEAA;
constexpr uint16_t kId1As16 = 0x0212;
constexpr char kId1AsString[] = "00000212-0000-1000-8000-00805f9b34fb";
constexpr uint16_t kId2As16 = 0x1122;
constexpr char kId3AsString[] = "12341234-0000-1000-8000-00805f9b34fb";
constexpr size_t kRandomDataSize = 100;
TEST(GAP_AdvertisingDataTest, ReaderEmptyData) {
BufferView empty;
AdvertisingDataReader reader(empty);
EXPECT_FALSE(reader.is_valid());
EXPECT_FALSE(reader.HasMoreData());
}
TEST(GAP_AdvertisingDataTest, MakeEmpty) {
AdvertisingData data;
EXPECT_EQ(0u, data.CalculateBlockSize());
}
TEST(GAP_AdvertisingDataTest, EncodeKnownURI) {
AdvertisingData data;
data.AddURI("https://abc.xyz");
auto bytes = CreateStaticByteBuffer(0x0B, 0x24, 0x17, '/', '/', 'a', 'b', 'c',
'.', 'x', 'y', 'z');
EXPECT_EQ(bytes.size(), data.CalculateBlockSize());
DynamicByteBuffer block(data.CalculateBlockSize());
data.WriteBlock(&block);
EXPECT_TRUE(ContainersEqual(bytes, block));
}
TEST(GAP_AdvertisingDataTest, EncodeUnknownURI) {
AdvertisingData data;
data.AddURI("flubs:xyz");
auto bytes = CreateStaticByteBuffer(0x0B, 0x24, 0x01, 'f', 'l', 'u', 'b', 's',
':', 'x', 'y', 'z');
size_t block_size = data.CalculateBlockSize();
EXPECT_EQ(bytes.size(), block_size);
DynamicByteBuffer block(block_size);
data.WriteBlock(&block);
EXPECT_TRUE(ContainersEqual(bytes, block));
}
TEST(GAP_AdvertisingDataTest, CompressServiceUUIDs) {
AdvertisingData data;
data.AddServiceUuid(UUID(kId1As16));
data.AddServiceUuid(UUID(kId2As16));
EXPECT_EQ(1 + 1 + (sizeof(uint16_t) * 2), data.CalculateBlockSize());
auto bytes = CreateStaticByteBuffer(0x05, 0x02, 0x12, 0x02, 0x22, 0x11);
size_t block_size = data.CalculateBlockSize();
EXPECT_EQ(bytes.size(), block_size);
DynamicByteBuffer block(block_size);
data.WriteBlock(&block);
EXPECT_TRUE(ContainersEqual(bytes, block));
}
TEST(GAP_AdvertisingDataTest, ParseBlock) {
auto bytes = CreateStaticByteBuffer(
// Complete 16-bit UUIDs
0x05, 0x03, 0x12, 0x02, 0x22, 0x11,
// Incomplete list of 32-bit UUIDs
0x05, 0x04, 0x34, 0x12, 0x34, 0x12,
// Local name
0x09, 0x09, 'T', 'e', 's', 't', 0xF0, 0x9F, 0x92, 0x96,
// TX Power
0x02, 0x0A, 0x8F);
AdvertisingData data;
EXPECT_TRUE(AdvertisingData::FromBytes(bytes, &data));
EXPECT_EQ(3u, data.service_uuids().size());
EXPECT_TRUE(data.local_name());
EXPECT_EQ("Test💖", *(data.local_name()));
EXPECT_TRUE(data.tx_power());
EXPECT_EQ(-113, *(data.tx_power()));
}
TEST(GAP_AdvertisingDataTest, ParseFIDL) {
fuchsia::bluetooth::le::AdvertisingData fidl_ad;
// Confirming UTF-8 codepoints are working as well.
fidl_ad.name = "Test💖";
fidl_ad.service_uuids.push_back(kId1AsString);
fidl_ad.service_uuids.push_back(kId3AsString);
auto svc_data = fidl::VectorPtr<uint8_t>::New(4);
for (size_t i = 0; i < svc_data->size(); i++) {
svc_data->at(i) = static_cast<uint8_t>(i * 3);
}
fuchsia::bluetooth::le::ServiceDataEntry service_data_entry;
service_data_entry.uuid = kId1AsString;
service_data_entry.data = std::move(svc_data);
fidl_ad.service_data.push_back(std::move(service_data_entry));
AdvertisingData data;
EXPECT_TRUE(AdvertisingData::FromFidl(fidl_ad, &data));
ASSERT_EQ(2u, data.service_uuids().size());
EXPECT_EQ("Test💖", *(data.local_name()));
UUID uuid1(kId1As16);
EXPECT_EQ(1u, data.service_data_uuids().size());
EXPECT_EQ(4u, data.service_data(uuid1).size());
EXPECT_FALSE(data.tx_power());
}
TEST(GAP_AdvertisingDataTest, ParseFIDLFailsWithMalformedUuid) {
fuchsia::bluetooth::le::AdvertisingData fidl_ad;
fidl_ad.service_uuids.push_back("12");
AdvertisingData data;
EXPECT_FALSE(AdvertisingData::FromFidl(fidl_ad, &data));
}
TEST(GAP_AdvertisingDataTest, ParseFIDLFailsWithMalformedServiceDataUuid) {
fuchsia::bluetooth::le::AdvertisingData fidl_ad;
auto svc_data = fidl::VectorPtr<uint8_t>::New(1);
fuchsia::bluetooth::le::ServiceDataEntry service_data_entry;
service_data_entry.uuid = "12";
service_data_entry.data = std::move(svc_data);
fidl_ad.service_data.push_back(std::move(service_data_entry));
AdvertisingData data;
EXPECT_FALSE(AdvertisingData::FromFidl(fidl_ad, &data));
}
TEST(GAP_AdvertisingDataTest, ManufacturerZeroLength) {
auto bytes = CreateStaticByteBuffer(
// Complete 16-bit UUIDs
0x05, 0x03, 0x12, 0x02, 0x22, 0x11,
// Manufacturer Data with no data
0x03, 0xFF, 0x34, 0x12);
AdvertisingData data;
EXPECT_EQ(0u, data.manufacturer_data_ids().size());
EXPECT_TRUE(AdvertisingData::FromBytes(bytes, &data));
EXPECT_EQ(1u, data.manufacturer_data_ids().count(0x1234));
EXPECT_EQ(0u, data.manufacturer_data(0x1234).size());
}
TEST(GAP_AdvertisingDataTest, ServiceData) {
// A typical Eddystone-URL beacon advertisement
// to "https://fuchsia.cl"
auto bytes = CreateStaticByteBuffer(
// Complete 16-bit UUIDs, 0xFEAA
0x03, 0x03, 0xAA, 0xFE,
// Eddystone Service (0xFEAA) Data:
0x10, 0x16, 0xAA, 0xFE,
0x10, // Eddystone-Uri type
0xEE, // TX Power level -18dBm
0x03, // "https://"
'f', 'u', 'c', 'h', 's', 'i', 'a', '.', 'c', 'l');
AdvertisingData data;
UUID eddystone((uint16_t)0xFEAA);
EXPECT_EQ(0u, data.service_data_uuids().size());
EXPECT_TRUE(AdvertisingData::FromBytes(bytes, &data));
EXPECT_EQ(1u, data.service_data_uuids().size());
EXPECT_EQ(13u, data.service_data(eddystone).size());
EXPECT_TRUE(ContainersEqual(bytes.view(8), data.service_data(eddystone)));
}
TEST(GAP_AdvertisingDataTest, Equality) {
AdvertisingData one, two;
UUID gatt(kGattUuid);
UUID eddy(kEddystoneUuid);
// Service UUIDs
EXPECT_EQ(two, one);
one.AddServiceUuid(gatt);
EXPECT_NE(two, one);
two.AddServiceUuid(gatt);
EXPECT_EQ(two, one);
// Even when the bytes are the same but from different places
auto bytes = CreateStaticByteBuffer(0x01, 0x02, 0x03, 0x04);
auto same = CreateStaticByteBuffer(0x01, 0x02, 0x03, 0x04);
two.SetManufacturerData(0x0123, bytes.view());
EXPECT_NE(two, one);
one.SetManufacturerData(0x0123, same.view());
EXPECT_EQ(two, one);
// When TX Power is different
two.SetTxPower(-34);
EXPECT_NE(two, one);
one.SetTxPower(-30);
EXPECT_NE(two, one);
one.SetTxPower(-34);
EXPECT_EQ(two, one);
// Even if the fields were added in different orders
AdvertisingData three, four;
three.AddServiceUuid(eddy);
three.AddServiceUuid(gatt);
EXPECT_NE(three, four);
four.AddServiceUuid(gatt);
four.AddServiceUuid(eddy);
EXPECT_EQ(three, four);
}
TEST(GAP_AdvertisingDataTest, Copy) {
UUID gatt(kGattUuid);
UUID eddy(kEddystoneUuid);
StaticByteBuffer<kRandomDataSize> rand_data;
rand_data.FillWithRandomBytes();
AdvertisingData source;
source.AddURI("http://fuchsia.cl");
source.AddURI("https://ru.st");
source.SetManufacturerData(0x0123, rand_data.view());
source.AddServiceUuid(gatt);
source.AddServiceUuid(eddy);
AdvertisingData dest;
source.Copy(&dest);
EXPECT_EQ(source, dest);
// Modifying the source shouldn't mess with the copy
source.SetLocalName("fuchsia");
EXPECT_FALSE(dest.local_name());
auto bytes = CreateStaticByteBuffer(0x01, 0x02, 0x03);
source.SetManufacturerData(0x0123, bytes.view());
EXPECT_TRUE(ContainersEqual(rand_data, dest.manufacturer_data(0x0123)));
}
TEST(GAP_AdvertisingDataTest, Move) {
UUID gatt(kGattUuid);
UUID eddy(kEddystoneUuid);
StaticByteBuffer<kRandomDataSize> rand_data;
rand_data.FillWithRandomBytes();
AdvertisingData source;
source.AddURI("http://fuchsia.cl");
source.AddURI("https://ru.st");
source.SetManufacturerData(0x0123, rand_data.view());
source.AddServiceUuid(gatt);
source.AddServiceUuid(eddy);
AdvertisingData dest = std::move(source);
// source should be empty.
EXPECT_EQ(AdvertisingData(), source);
// Dest should have the data we set.
EXPECT_EQ(
std::unordered_set<std::string>({"http://fuchsia.cl", "https://ru.st"}),
dest.uris());
EXPECT_TRUE(ContainersEqual(rand_data, dest.manufacturer_data(0x0123)));
EXPECT_EQ(std::unordered_set<UUID>({gatt, eddy}), dest.service_uuids());
}
TEST(GAP_AdvertisingDataTest, Uris) {
auto bytes = CreateStaticByteBuffer(
// Uri: "https://abc.xyz"
0x0B, 0x24, 0x17, '/', '/', 'a', 'b', 'c', '.', 'x', 'y', 'z',
// Uri: "flubs:abc"
0x0B, 0x24, 0x01, 'f', 'l', 'u', 'b', 's', ':', 'a', 'b', 'c');
AdvertisingData data;
EXPECT_TRUE(AdvertisingData::FromBytes(bytes, &data));
auto uris = data.uris();
EXPECT_EQ(2u, uris.size());
EXPECT_TRUE(std::find(uris.begin(), uris.end(), "https://abc.xyz") !=
uris.end());
EXPECT_TRUE(std::find(uris.begin(), uris.end(), "flubs:abc") != uris.end());
}
TEST(GAP_AdvertisingDataTest, ReaderMalformedData) {
// TLV length exceeds the size of the payload
auto bytes0 = CreateStaticByteBuffer(0x01);
AdvertisingDataReader reader(bytes0);
EXPECT_FALSE(reader.is_valid());
EXPECT_FALSE(reader.HasMoreData());
auto bytes = CreateStaticByteBuffer(0x05, 0x00, 0x00, 0x00, 0x00);
reader = AdvertisingDataReader(bytes);
EXPECT_FALSE(reader.is_valid());
EXPECT_FALSE(reader.HasMoreData());
// TLV length is 0. This is not considered malformed. Data should be valid but
// should not return more data.
bytes = CreateStaticByteBuffer(0x00, 0x00, 0x00, 0x00, 0x00);
reader = AdvertisingDataReader(bytes);
EXPECT_TRUE(reader.is_valid());
EXPECT_FALSE(reader.HasMoreData());
// First field is valid, second field is not.
DataType type;
BufferView data;
bytes = CreateStaticByteBuffer(0x02, 0x00, 0x00, 0x02, 0x00);
reader = AdvertisingDataReader(bytes);
EXPECT_FALSE(reader.is_valid());
EXPECT_FALSE(reader.HasMoreData());
EXPECT_FALSE(reader.GetNextField(&type, &data));
// First field is valid, second field has length 0.
bytes = CreateStaticByteBuffer(0x02, 0x00, 0x00, 0x00, 0x00);
reader = AdvertisingDataReader(bytes);
EXPECT_TRUE(reader.is_valid());
EXPECT_TRUE(reader.HasMoreData());
EXPECT_TRUE(reader.GetNextField(&type, &data));
EXPECT_FALSE(reader.HasMoreData());
EXPECT_FALSE(reader.GetNextField(&type, &data));
}
TEST(GAP_AdvertisingDataTest, ReaderParseFields) {
auto bytes =
CreateStaticByteBuffer(0x02, 0x01, 0x00, 0x05, 0x09, 'T', 'e', 's', 't');
AdvertisingDataReader reader(bytes);
EXPECT_TRUE(reader.is_valid());
EXPECT_TRUE(reader.HasMoreData());
DataType type;
BufferView data;
EXPECT_TRUE(reader.GetNextField(&type, &data));
EXPECT_EQ(DataType::kFlags, type);
EXPECT_EQ(1u, data.size());
EXPECT_TRUE(ContainersEqual(CreateStaticByteBuffer(0x00), data));
EXPECT_TRUE(reader.HasMoreData());
EXPECT_TRUE(reader.GetNextField(&type, &data));
EXPECT_EQ(DataType::kCompleteLocalName, type);
EXPECT_EQ(4u, data.size());
EXPECT_TRUE(ContainersEqual(std::string("Test"), data));
EXPECT_FALSE(reader.HasMoreData());
EXPECT_FALSE(reader.GetNextField(&type, &data));
}
// Helper for computing the size of a string literal at compile time. sizeof()
// would have worked too but that counts the null character.
template <std::size_t N>
constexpr size_t StringSize(char const (&str)[N]) {
return N - 1;
}
TEST(GAP_AdvertisingDataTest, WriteField) {
constexpr char kValue0[] = "value zero";
constexpr char kValue1[] = "value one";
constexpr char kValue2[] = "value two";
constexpr char kValue3[] = "value three";
// Have just enough space for the first three values (+ 6 for 2 extra octets
// for each TLV field).
constexpr char kBufferSize =
StringSize(kValue0) + StringSize(kValue1) + StringSize(kValue2) + 6;
StaticByteBuffer<kBufferSize> buffer;
AdvertisingDataWriter writer(&buffer);
EXPECT_EQ(0u, writer.bytes_written());
// We write malformed values here for testing purposes.
EXPECT_TRUE(writer.WriteField(DataType::kFlags, BufferView(kValue0)));
EXPECT_EQ(StringSize(kValue0) + 2, writer.bytes_written());
EXPECT_TRUE(
writer.WriteField(DataType::kShortenedLocalName, BufferView(kValue1)));
EXPECT_EQ(StringSize(kValue0) + 2 + StringSize(kValue1) + 2,
writer.bytes_written());
// Trying to write kValue3 should fail because there isn't enough room left in
// the buffer.
EXPECT_FALSE(
writer.WriteField(DataType::kCompleteLocalName, BufferView(kValue3)));
// Writing kValue2 should fill up the buffer.
EXPECT_TRUE(
writer.WriteField(DataType::kCompleteLocalName, BufferView(kValue2)));
EXPECT_FALSE(
writer.WriteField(DataType::kCompleteLocalName, BufferView(kValue3)));
EXPECT_EQ(buffer.size(), writer.bytes_written());
// Verify the contents.
DataType type;
BufferView value;
AdvertisingDataReader reader(buffer);
EXPECT_TRUE(reader.is_valid());
EXPECT_TRUE(reader.GetNextField(&type, &value));
EXPECT_EQ(DataType::kFlags, type);
EXPECT_EQ(kValue0, value.AsString());
EXPECT_TRUE(reader.GetNextField(&type, &value));
EXPECT_EQ(DataType::kShortenedLocalName, type);
EXPECT_EQ(kValue1, value.AsString());
EXPECT_TRUE(reader.GetNextField(&type, &value));
EXPECT_EQ(DataType::kCompleteLocalName, type);
EXPECT_EQ(kValue2, value.AsString());
EXPECT_FALSE(reader.GetNextField(&type, &value));
}
} // namespace
} // namespace gap
} // namespace bt