blob: d9a2f8820995e051af1474dcd48afeb267b81a31 [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_host.h"
#include "fidl/gatt_client_server.h"
#include "fidl/gatt_server_server.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
using namespace bt;
namespace bthost {
GattHost::GattHost() : GattHost::GattHost(gatt::GATT::Create()) {}
GattHost::GattHost(std::unique_ptr<gatt::GATT> gatt)
: gatt_(std::move(gatt)), weak_ptr_factory_(this) {
ZX_ASSERT(gatt_);
gatt_->RegisterRemoteServiceWatcher(
[self = weak_ptr_factory_.GetWeakPtr()](auto peer_id, auto service) {
// Make sure we are not holding the lock while |watcher| executes to
// prevent potential deadlocks.
bt::gatt::GATT::RemoteServiceWatcher watcher;
{
std::lock_guard<std::mutex> lock(self->mtx_);
if (self->remote_service_watcher_) {
watcher = self->remote_service_watcher_.share();
}
}
if (watcher) {
watcher(peer_id, service);
}
});
}
GattHost::~GattHost() {
// Stop processing further GATT profile requests.
gatt_ = nullptr;
// Clear the remote device callback to prevent further notifications after
// this call.
{
std::lock_guard<std::mutex> lock(mtx_);
remote_service_watcher_ = {};
}
CloseServers();
}
void GattHost::CloseServers() {
// This closes all remaining FIDL channels.
client_servers_.clear();
server_servers_.clear();
}
void GattHost::BindGattServer(fidl::InterfaceRequest<fuchsia::bluetooth::gatt::Server> request) {
auto self = weak_ptr_factory_.GetWeakPtr();
auto server = std::make_unique<GattServerServer>(gatt_->AsWeakPtr(), std::move(request));
server->set_error_handler([self, server = server.get()](zx_status_t status) {
if (self) {
bt_log(DEBUG, "bt-host", "GATT server disconnected");
self->server_servers_.erase(server);
}
});
server_servers_[server.get()] = std::move(server);
}
void GattHost::BindGattClient(Token token, bt::gatt::PeerId peer_id,
fidl::InterfaceRequest<fuchsia::bluetooth::gatt::Client> request) {
// Either initialize a new entry for |token| or obtain an iterator to the
// existing entry. In either case the iterator will be valid.
auto inner_iter = client_servers_.try_emplace(token, ClientMap()).first;
if (inner_iter->second.find(peer_id) != inner_iter->second.end()) {
bt_log(WARN, "bt-host", "only 1 gatt.Client FIDL handle allowed per peer (%s) per token (%lu)!",
bt_str(peer_id), token);
// The handle owned by |request| will be closed.
return;
}
auto self = weak_ptr_factory_.GetWeakPtr();
auto server = std::make_unique<GattClientServer>(peer_id, gatt_->AsWeakPtr(), std::move(request));
server->set_error_handler([self, token, peer_id](zx_status_t status) {
if (self) {
bt_log(DEBUG, "bt-host", "GATT client disconnected");
self->UnbindGattClient(token, {peer_id});
}
});
inner_iter->second[peer_id] = std::move(server);
}
void GattHost::UnbindGattClient(Token token, std::optional<bt::gatt::PeerId> peer_id) {
if (!peer_id.has_value()) {
client_servers_.erase(token);
return;
}
auto iter = client_servers_.find(token);
if (iter != client_servers_.end()) {
iter->second.erase(*peer_id);
if (iter->second.empty()) {
client_servers_.erase(iter);
}
}
}
void GattHost::SetRemoteServiceWatcher(bt::gatt::GATT::RemoteServiceWatcher callback) {
std::lock_guard<std::mutex> lock(mtx_);
remote_service_watcher_ = std::move(callback);
}
} // namespace bthost