// Copyright 2024 The Pigweed Authors // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. #include "pw_bluetooth_sapphire/fuchsia/host/fidl/gatt_client_server.h" #include #include #include "pw_bluetooth_sapphire/fuchsia/host/fidl/gatt_remote_service_server.h" #include "pw_bluetooth_sapphire/fuchsia/host/fidl/helpers.h" #include "pw_bluetooth_sapphire/internal/host/common/log.h" #include "pw_bluetooth_sapphire/internal/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, bt::gatt::GATT::WeakPtr gatt, fidl::InterfaceRequest request) : GattServerBase(std::move(gatt), this, std::move(request)), peer_id_(peer_id), weak_self_(this) {} void GattClientServer::ListServices(::fidl::VectorPtr<::std::string> fidl_uuids, ListServicesCallback callback) { // Parse the UUID list. std::vector 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])) { bt_log(WARN, "fidl", "%s: Invalid UUID: %s (peer: %s)", __FUNCTION__, fidl_uuids.value()[i].c_str(), bt_str(peer_id_)); callback(fidl_helpers::NewFidlError( ErrorCode::INVALID_ARGUMENTS, "Invalid UUID: " + fidl_uuids.value()[i]), std::vector(static_cast(0u))); return; } } } auto cb = [callback = std::move(callback), peer_id = peer_id_, func = __FUNCTION__](bt::att::Result<> status, auto services) { std::vector out; if (status.is_error()) { bt_log(WARN, "fidl", "%s: Failed to discover services (peer: %s)", func, bt_str(peer_id)); auto fidl_status = fidl_helpers::ResultToFidlDeprecated( 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 = svc->info().kind == bt::gatt::ServiceKind::PRIMARY; 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 request) { if (connected_services_.count(id)) { bt_log(WARN, "fidl", "%s: service already requested (service: %lu, peer: %s)", __FUNCTION__, id, bt_str(peer_id_)); return; } // Initialize an entry so that we remember when this request is in progress. connected_services_[id] = nullptr; bt::gatt::RemoteService::WeakPtr service = gatt()->FindService(peer_id_, id); // Automatically called on failure. auto fail_cleanup = fit::defer([this, id] { connected_services_.erase(id); }); if (!service.is_alive()) { bt_log(WARN, "fidl", "%s: failed (service: %lu, peer: %s)", __FUNCTION__, id, bt_str(peer_id_)); return; } // Clean up the server if either the peer device or the FIDL client // disconnects. auto self = weak_self_.GetWeakPtr(); const char* func = __FUNCTION__; auto error_cb = [self, id, peer_id = peer_id_, func] { bt_log(DEBUG, "fidl", "%s: service disconnected (service: %lu, peer: %s)", func, id, bt_str(peer_id)); if (self.is_alive()) { self->connected_services_.erase(id); } }; if (!service->AddRemovedHandler(error_cb)) { bt_log(WARN, "fidl", "%s: failed to assign closed handler (service: %lu, peer: %s)", func, id, bt_str(self->peer_id_)); return; } fail_cleanup.cancel(); auto server = std::make_unique( service->GetWeakPtr(), gatt(), peer_id_, std::move(request)); server->set_error_handler( [cb = std::move(error_cb)](zx_status_t status) { cb(); }); self->connected_services_[id] = std::move(server); } } // namespace bthost