blob: 294d2e4ebea1939430b7387b3b112ab0201819ad [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 "src/connectivity/bluetooth/core/bt-host/gatt/gatt_defs.h"
namespace bt {
namespace gatt {
namespace {
void NopReadHandler(IdType, IdType, uint16_t, const ReadResponder&) {}
void NopWriteHandler(IdType, IdType, uint16_t, const ByteBuffer&,
const WriteResponder&) {}
void NopCCCallback(IdType, IdType, PeerId, bool notify, bool indicate) {}
void NopSendIndication(PeerId, att::Handle, const ByteBuffer&) {}
// Handles for the third attribute (Service Changed characteristic) and fourth
// attribute (corresponding client config).
constexpr att::Handle kChrcHandle = 0x0003;
constexpr att::Handle kCCCHandle = 0x0004;
constexpr PeerId kTestPeerId(1);
constexpr uint16_t kEnableInd = 0x0002;
class GATT_GenericAttributeServiceTest : public ::testing::Test {
protected:
bool WriteServiceChangedCCC(PeerId peer_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(peer_id, 0u, 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 = [&](PeerId peer_id, att::Handle handle,
const ByteBuffer& value) {
EXPECT_EQ(kTestPeerId, 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(kTestPeerId, kEnableInd, &ecode);
EXPECT_EQ(0, callback_count);
constexpr UUID kTestSvcType((uint32_t)0xdeadbeef);
constexpr IdType kChrcId = 0;
constexpr uint8_t kChrcProps = Property::kRead;
constexpr 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 = [&](PeerId peer_id, att::Handle handle,
const ByteBuffer& value) {
EXPECT_EQ(kTestPeerId, 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 UUID kTestSvcType((uint32_t)0xdeadbeef);
constexpr IdType kChrcId = 0;
constexpr uint8_t kChrcProps = Property::kNotify;
constexpr 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(kTestPeerId, kEnableInd, &ecode);
EXPECT_EQ(0, callback_count);
mgr.UnregisterService(service_id);
EXPECT_EQ(1, callback_count);
}
} // namespace
} // namespace gatt
} // namespace bt