| // 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 |