blob: 484c37b20e1d234047cc85e23a2247c595017462 [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 "generic_attribute_service.h"
#include <zircon/assert.h>
#include "gtest/gtest.h"
#include "garnet/drivers/bluetooth/lib/gatt/gatt_defs.h"
namespace btlib {
namespace gatt {
namespace {
void NopReadHandler(IdType, IdType, uint16_t, const ReadResponder&) {}
void NopWriteHandler(IdType,
IdType,
uint16_t,
const common::ByteBuffer&,
const WriteResponder&) {}
void NopCCCallback(IdType service_id,
IdType chrc_id,
const std::string& peer_id,
bool notify,
bool indicate) {}
static void NopSendIndication(const std::string&,
att::Handle,
const common::ByteBuffer&) {}
// Handles for the third attribute (Service Changed characteristic) and fourth
// attribute (corresponding client config).
static constexpr att::Handle kChrcHandle = 0x0003;
static constexpr att::Handle kCCCHandle = 0x0004;
static constexpr char kTestDeviceId[] = "11223344-1122-1122-1122-112233445566";
static constexpr uint16_t kEnableInd = 0x0002;
class GATT_GenericAttributeServiceTest : public ::testing::Test {
protected:
bool WriteServiceChangedCCC(const std::string& device_id,
uint16_t ccc_value,
att::ErrorCode* out_ecode) {
ZX_DEBUG_ASSERT(out_ecode);
auto* attr = mgr.database()->FindAttribute(kCCCHandle);
ZX_DEBUG_ASSERT(attr);
auto result_cb = [&out_ecode](auto cb_code) { *out_ecode = cb_code; };
uint16_t value = htole16(ccc_value);
return attr->WriteAsync(
device_id, 0u, common::BufferView(&value, sizeof(value)), result_cb);
}
LocalServiceManager mgr;
};
// Test registration and unregistration of the local GATT service itself.
TEST_F(GATT_GenericAttributeServiceTest, RegisterUnregister) {
{
GenericAttributeService gatt_service(&mgr, NopSendIndication);
// Check that the local attribute database has a grouping for the GATT GATT
// service with four attributes.
auto iter = mgr.database()->groupings().begin();
EXPECT_TRUE(iter->complete());
EXPECT_EQ(4u, iter->attributes().size());
EXPECT_TRUE(iter->active());
EXPECT_EQ(0x0001, iter->start_handle());
EXPECT_EQ(0x0004, iter->end_handle());
EXPECT_EQ(types::kPrimaryService, iter->group_type());
auto const* ccc_attr = mgr.database()->FindAttribute(kCCCHandle);
ASSERT_TRUE(ccc_attr != nullptr);
EXPECT_EQ(types::kClientCharacteristicConfig, ccc_attr->type());
}
// The service should now be unregistered, so no characeteristic attributes
// should be active.
auto const* chrc_attr = mgr.database()->FindAttribute(kChrcHandle);
ASSERT_TRUE(chrc_attr == nullptr);
}
// Tests that registering the GATT service, enabling indication on its Service
// Changed characteristic, then registering a different service invokes the
// callback to send an indication to the "client."
TEST_F(GATT_GenericAttributeServiceTest, IndicateOnRegister) {
int callback_count = 0;
auto send_indication = [&](const std::string& peer_id,
att::Handle handle,
const common::ByteBuffer& value) {
EXPECT_EQ(kTestDeviceId, peer_id);
EXPECT_EQ(kChrcHandle, handle);
ASSERT_EQ(4u, value.size());
// The second service following the four-attribute GATT service should span
// the subsequent three handles.
EXPECT_EQ(0x05, value[0]);
EXPECT_EQ(0x00, value[1]);
EXPECT_EQ(0x07, value[2]);
EXPECT_EQ(0x00, value[3]);
callback_count++;
};
// Register the GATT service.
GenericAttributeService gatt_service(&mgr, std::move(send_indication));
// Enable Service Changed indications for the test client.
att::ErrorCode ecode;
WriteServiceChangedCCC(kTestDeviceId, kEnableInd, &ecode);
EXPECT_EQ(0, callback_count);
constexpr common::UUID kTestSvcType((uint32_t)0xdeadbeef);
constexpr IdType kChrcId = 0;
constexpr uint8_t kChrcProps = Property::kRead;
constexpr common::UUID kTestChrcType((uint32_t)0xdeadbeef);
const att::AccessRequirements kReadReqs(true, true, true);
const att::AccessRequirements kWriteReqs, kUpdateReqs;
auto service = std::make_unique<Service>(false /* primary */, kTestSvcType);
service->AddCharacteristic(
std::make_unique<Characteristic>(kChrcId, kTestChrcType, kChrcProps, 0,
kReadReqs, kWriteReqs, kUpdateReqs));
auto service_id = mgr.RegisterService(std::move(service), NopReadHandler,
NopWriteHandler, NopCCCallback);
EXPECT_NE(0u, service_id);
EXPECT_EQ(1, callback_count);
}
// Same test as above, but the indication is enabled just prior unregistering
// the latter test service.
TEST_F(GATT_GenericAttributeServiceTest, IndicateOnUnregister) {
int callback_count = 0;
auto send_indication = [&](const std::string& peer_id,
att::Handle handle,
const common::ByteBuffer& value) {
EXPECT_EQ(kTestDeviceId, peer_id);
EXPECT_EQ(kChrcHandle, handle);
ASSERT_EQ(4u, value.size());
// The second service following the four-attribute GATT service should span
// the subsequent four handles (update enabled).
EXPECT_EQ(0x05, value[0]);
EXPECT_EQ(0x00, value[1]);
EXPECT_EQ(0x08, value[2]);
EXPECT_EQ(0x00, value[3]);
callback_count++;
};
// Register the GATT service.
GenericAttributeService gatt_service(&mgr, std::move(send_indication));
constexpr common::UUID kTestSvcType((uint32_t)0xdeadbeef);
constexpr IdType kChrcId = 0;
constexpr uint8_t kChrcProps = Property::kNotify;
constexpr common::UUID kTestChrcType((uint32_t)0xdeadbeef);
const att::AccessRequirements kReadReqs, kWriteReqs;
const att::AccessRequirements kUpdateReqs(true, true, true);
auto service = std::make_unique<Service>(false /* primary */, kTestSvcType);
service->AddCharacteristic(
std::make_unique<Characteristic>(kChrcId, kTestChrcType, kChrcProps, 0,
kReadReqs, kWriteReqs, kUpdateReqs));
auto service_id = mgr.RegisterService(std::move(service), NopReadHandler,
NopWriteHandler, NopCCCallback);
EXPECT_NE(0u, service_id);
// Enable Service Changed indications for the test client.
att::ErrorCode ecode;
WriteServiceChangedCCC(kTestDeviceId, kEnableInd, &ecode);
EXPECT_EQ(0, callback_count);
mgr.UnregisterService(service_id);
EXPECT_EQ(1, callback_count);
}
} // namespace
} // namespace gatt
} // namespace btlib