blob: c6a65b58047d0dda83e9124c134c7c433119ea95 [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 "gatt.h"
#include <unordered_map>
#include <zircon/assert.h>
#include "garnet/drivers/bluetooth/lib/att/bearer.h"
#include "garnet/drivers/bluetooth/lib/common/log.h"
#include "garnet/drivers/bluetooth/lib/common/task_domain.h"
#include "garnet/drivers/bluetooth/lib/gatt/generic_attribute_service.h"
#include "garnet/drivers/bluetooth/lib/l2cap/channel.h"
#include "client.h"
#include "connection.h"
#include "remote_service.h"
#include "server.h"
namespace btlib {
namespace gatt {
namespace {
class Impl final : public GATT, common::TaskDomain<Impl, GATT> {
using TaskDomainBase = common::TaskDomain<Impl, GATT>;
public:
explicit Impl(async_dispatcher_t* gatt_dispatcher)
: TaskDomainBase(this, gatt_dispatcher), initialized_(false) {}
~Impl() override {
ZX_DEBUG_ASSERT_MSG(!initialized_, "ShutDown() must have been called!");
}
// GATT overrides:
void Initialize() override {
PostMessage([this] {
ZX_DEBUG_ASSERT(!initialized_);
local_services_ = std::make_unique<LocalServiceManager>();
// Forwards Service Changed payloads to clients.
auto send_indication_callback = [this](const std::string& peer_id,
att::Handle handle,
const common::ByteBuffer& value) {
auto iter = connections_.find(peer_id);
if (iter == connections_.end()) {
bt_log(WARN, "gatt", "peer not registered: %s", peer_id.c_str());
return;
}
iter->second.server()->SendNotification(handle, value.view(), true);
};
// Spin up Generic Attribute as the first service.
gatt_service_ = std::make_unique<GenericAttributeService>(
local_services_.get(), std::move(send_indication_callback));
initialized_ = true;
bt_log(TRACE, "gatt", "initialized");
});
}
void ShutDown() override { TaskDomainBase::ScheduleCleanUp(); }
// Called on the GATT runner as a result of ScheduleCleanUp().
void CleanUp() {
bt_log(TRACE, "gatt", "shutting down");
initialized_ = false;
connections_.clear();
gatt_service_ = nullptr;
local_services_ = nullptr;
remote_service_callbacks_.clear();
}
void AddConnection(const std::string& peer_id,
fbl::RefPtr<l2cap::Channel> att_chan) override {
bt_log(TRACE, "gatt", "add connection %s", peer_id.c_str());
PostMessage([this, peer_id, att_chan] {
ZX_DEBUG_ASSERT(local_services_);
auto iter = connections_.find(peer_id);
if (iter != connections_.end()) {
bt_log(WARN, "gatt", "peer is already registered: %s", peer_id.c_str());
return;
}
auto att_bearer = att::Bearer::Create(att_chan);
if (!att_bearer) {
// This can happen if the link closes before the Bearer activates the
// channel.
bt_log(ERROR, "gatt", "failed to initialize ATT bearer");
att_chan->SignalLinkError();
return;
}
connections_[peer_id] =
internal::Connection(peer_id, att_bearer, local_services_->database(),
std::bind(&Impl::OnServiceAdded, this, peer_id,
std::placeholders::_1),
dispatcher());
});
}
void RemoveConnection(std::string peer_id) override {
bt_log(TRACE, "gatt", "remove connection: %s", peer_id.c_str());
PostMessage(
[this, peer_id = std::move(peer_id)] {
local_services_->DisconnectClient(peer_id);
connections_.erase(peer_id);
});
}
void RegisterService(ServicePtr service,
ServiceIdCallback callback,
ReadHandler read_handler,
WriteHandler write_handler,
ClientConfigCallback ccc_callback) override {
PostMessage([this, svc = std::move(service), id_cb = std::move(callback),
rh = std::move(read_handler), wh = std::move(write_handler),
cccc = std::move(ccc_callback)]() mutable {
IdType id;
if (!initialized_) {
bt_log(TRACE, "gatt", "cannot register service after shutdown");
id = kInvalidId;
} else {
id = local_services_->RegisterService(std::move(svc), std::move(rh),
std::move(wh), std::move(cccc));
}
id_cb(id);
});
}
void UnregisterService(IdType service_id) override {
PostMessage([this, service_id] {
if (!initialized_)
return;
ZX_DEBUG_ASSERT(local_services_);
local_services_->UnregisterService(service_id);
});
}
void SendNotification(IdType service_id,
IdType chrc_id,
std::string peer_id,
::std::vector<uint8_t> value,
bool indicate) override {
PostMessage([this, svc_id = service_id, chrc_id, indicate,
peer_id = std::move(peer_id), value = std::move(value)] {
if (!initialized_) {
bt_log(SPEW, "gatt", "cannot notify after shutdown");
return;
}
// There is nothing to do if the requested peer is not connected.
auto iter = connections_.find(peer_id);
if (iter == connections_.end()) {
bt_log(SPEW, "gatt", "cannot notify disconnected peer: %s",
peer_id.c_str());
return;
}
LocalServiceManager::ClientCharacteristicConfig config;
if (!local_services_->GetCharacteristicConfig(svc_id, chrc_id, peer_id,
&config)) {
bt_log(SPEW, "gatt", "peer has not configured characteristic: %s",
peer_id.c_str());
return;
}
// Make sure that the client has subscribed to the requested protocol
// method.
if ((indicate & !config.indicate) || (!indicate && !config.notify)) {
bt_log(SPEW, "gatt", "peer has no configuration (%s): %s",
(indicate ? "ind" : "not"), peer_id.c_str());
return;
}
iter->second.server()->SendNotification(
config.handle, common::BufferView(value.data(), value.size()),
indicate);
});
}
void DiscoverServices(std::string peer_id) override {
bt_log(TRACE, "gatt", "discover services: %s", peer_id.c_str());
PostMessage([this, peer_id = std::move(peer_id)] {
auto iter = connections_.find(peer_id);
if (iter == connections_.end()) {
bt_log(WARN, "gatt", "unknown peer: %s", peer_id.c_str());
return;
}
iter->second.Initialize();
});
}
void RegisterRemoteServiceWatcher(RemoteServiceWatcher callback,
async_dispatcher_t* dispatcher) override {
ZX_DEBUG_ASSERT(callback);
PostMessage(
[this, callback = std::move(callback), runner = dispatcher]() mutable {
if (initialized_) {
remote_service_callbacks_.emplace_back(std::move(callback),
std::move(runner));
}
});
}
void ListServices(std::string peer_id,
std::vector<common::UUID> uuids,
ServiceListCallback callback) override {
ZX_DEBUG_ASSERT(callback);
PostMessage([this, peer_id = std::move(peer_id),
callback = std::move(callback),
uuids = std::move(uuids)]() mutable {
auto iter = connections_.find(peer_id);
if (iter == connections_.end()) {
// Connection not found.
callback(att::Status(common::HostError::kNotFound), ServiceList());
return;
}
iter->second.remote_service_manager()->ListServices(uuids,
std::move(callback));
});
}
void FindService(std::string peer_id,
IdType service_id,
RemoteServiceCallback callback) override {
PostMessage([this, service_id, peer_id = std::move(peer_id),
callback = std::move(callback)]() mutable {
auto iter = connections_.find(peer_id);
if (iter == connections_.end()) {
// Connection not found.
callback(nullptr);
return;
}
callback(iter->second.remote_service_manager()->FindService(service_id));
});
}
private:
// Called when a new remote GATT service is discovered.
void OnServiceAdded(const std::string& peer_id,
fbl::RefPtr<RemoteService> svc) {
bt_log(TRACE, "gatt", "service added (peer_id: %s, handle: %#.4x, uuid: %s",
peer_id.c_str(), svc->handle(), svc->uuid().ToString().c_str());
for (auto& handler : remote_service_callbacks_) {
handler.Notify(peer_id, svc);
}
}
// NOTE: The following objects MUST be initialized, accessed, and destroyed on
// the GATT thread. They are not thread safe.
bool initialized_;
// The registry containing all local GATT services. This represents a single
// ATT database.
std::unique_ptr<LocalServiceManager> local_services_;
// Local GATT service (first in database) for clients to subscribe to service
// registration and removal.
std::unique_ptr<GenericAttributeService> gatt_service_;
// Contains the state of all GATT profile connections and their services.
std::unordered_map<std::string, internal::Connection> connections_;
// All registered remote service handlers.
struct RemoteServiceHandler {
RemoteServiceHandler(RemoteServiceWatcher watcher, async_dispatcher_t* dispatcher)
: watcher_(std::move(watcher)), dispatcher_(dispatcher) {}
RemoteServiceHandler() = default;
RemoteServiceHandler(RemoteServiceHandler&&) = default;
void Notify(const std::string& peer_id, fbl::RefPtr<RemoteService> svc) {
if (!dispatcher_) {
watcher_(peer_id, std::move(svc));
return;
}
// NOTE: this makes a copy of |watcher_|.
async::PostTask(dispatcher_, [peer_id, watcher = watcher_.share(),
svc = std::move(svc)]() mutable {
watcher(peer_id, std::move(svc));
});
}
private:
RemoteServiceWatcher watcher_;
async_dispatcher_t* dispatcher_;
FXL_DISALLOW_COPY_AND_ASSIGN(RemoteServiceHandler);
};
std::vector<RemoteServiceHandler> remote_service_callbacks_;
FXL_DISALLOW_COPY_AND_ASSIGN(Impl);
};
} // namespace
// static
fbl::RefPtr<GATT> GATT::Create(async_dispatcher_t* gatt_dispatcher) {
return AdoptRef(new Impl(gatt_dispatcher));
}
} // namespace gatt
} // namespace btlib