blob: d8ede0647018208318a770b7e1d3ff5a5a79d6b1 [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_client_server.h"
#include <lib/fit/defer.h>
#include "gatt_remote_service_server.h"
#include "helpers.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/gatt/gatt.h"
using fuchsia::bluetooth::ErrorCode;
using fuchsia::bluetooth::Status;
using fuchsia::bluetooth::gatt::Client;
using fuchsia::bluetooth::gatt::RemoteService;
using fuchsia::bluetooth::gatt::ServiceInfo;
using fuchsia::bluetooth::gatt::ServiceInfoPtr;
namespace bthost {
GattClientServer::GattClientServer(bt::gatt::PeerId peer_id, fbl::RefPtr<bt::gatt::GATT> gatt,
fidl::InterfaceRequest<Client> request)
: GattServerBase(gatt, this, std::move(request)), peer_id_(peer_id), weak_ptr_factory_(this) {}
void GattClientServer::ListServices(::fidl::VectorPtr<::std::string> fidl_uuids,
ListServicesCallback callback) {
// Parse the UUID list.
std::vector<bt::UUID> uuids;
if (fidl_uuids.has_value()) {
// Allocate all at once and convert in-place.
uuids.resize(fidl_uuids->size());
for (size_t i = 0; i < uuids.size(); ++i) {
if (!StringToUuid(fidl_uuids.value()[i], &uuids[i])) {
callback(fidl_helpers::NewFidlError(ErrorCode::INVALID_ARGUMENTS,
"Invalid UUID: " + fidl_uuids.value()[i]),
std::vector<ServiceInfo>((size_t)0u));
return;
}
}
}
auto cb = [callback = std::move(callback)](bt::att::Status status, auto services) {
std::vector<ServiceInfo> out;
if (!status) {
auto fidl_status =
fidl_helpers::StatusToFidlDeprecated(status, "Failed to discover services");
callback(std::move(fidl_status), std::move(out));
return;
}
out.resize(services.size());
size_t i = 0;
for (const auto& svc : services) {
ServiceInfo service_info;
service_info.id = svc->handle();
service_info.primary = true;
service_info.type = svc->uuid().ToString();
out[i++] = std::move(service_info);
}
callback(Status(), std::move(out));
};
gatt()->ListServices(peer_id_, std::move(uuids), std::move(cb));
}
void GattClientServer::ConnectToService(uint64_t id,
::fidl::InterfaceRequest<RemoteService> service) {
if (connected_services_.count(id)) {
bt_log(TRACE, "bt-host", "service already requested");
return;
}
// Initialize an entry so that we remember when this request is in progress.
connected_services_[id] = nullptr;
auto self = weak_ptr_factory_.GetWeakPtr();
auto callback = [self, id, request = std::move(service)](auto service) mutable {
if (!self)
return;
// The operation must be in progress.
ZX_DEBUG_ASSERT(self->connected_services_.count(id));
// Automatically called on failure.
auto fail_cleanup = fit::defer([self, id] { self->connected_services_.erase(id); });
if (!service) {
bt_log(TRACE, "bt-host", "failed to connect to service");
return;
}
// Clean up the server if either the peer device or the FIDL client
// disconnects.
auto error_cb = [self, id] {
bt_log(TRACE, "bt-host", "service disconnected");
if (self) {
self->connected_services_.erase(id);
}
};
if (!service->AddRemovedHandler(error_cb)) {
bt_log(TRACE, "bt-host", "failed to assign closed handler");
return;
}
fail_cleanup.cancel();
auto server = std::make_unique<GattRemoteServiceServer>(std::move(service), self->gatt(),
std::move(request));
server->set_error_handler([cb = std::move(error_cb)](zx_status_t status) { cb(); });
self->connected_services_[id] = std::move(server);
};
gatt()->FindService(peer_id_, id, std::move(callback));
}
} // namespace bthost