| // 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 "remote_service_manager.h" |
| |
| #include "src/connectivity/bluetooth/core/bt-host/common/log.h" |
| #include "src/connectivity/bluetooth/core/bt-host/gatt/remote_service.h" |
| |
| namespace bt::gatt::internal { |
| |
| RemoteServiceManager::ServiceListRequest::ServiceListRequest(ServiceListCallback callback, |
| const std::vector<UUID>& uuids) |
| : callback_(std::move(callback)), uuids_(uuids) { |
| ZX_DEBUG_ASSERT(callback_); |
| } |
| |
| void RemoteServiceManager::ServiceListRequest::Complete(att::Status status, |
| const ServiceMap& services) { |
| TRACE_DURATION("bluetooth", "gatt::RemoteServiceManager::ServiceListRequest::Complete"); |
| |
| ServiceList result; |
| |
| if (!status || services.empty()) { |
| callback_(status, std::move(result)); |
| return; |
| } |
| |
| for (const auto& iter : services) { |
| auto& svc = iter.second; |
| auto pred = [&svc](const UUID& uuid) { return svc->uuid() == uuid; }; |
| if (uuids_.empty() || std::find_if(uuids_.begin(), uuids_.end(), pred) != uuids_.end()) { |
| result.push_back(iter.second); |
| } |
| } |
| |
| callback_(status, std::move(result)); |
| } |
| |
| RemoteServiceManager::RemoteServiceManager(std::unique_ptr<Client> client, |
| async_dispatcher_t* gatt_dispatcher) |
| : gatt_dispatcher_(gatt_dispatcher), |
| client_(std::move(client)), |
| initialized_(false), |
| weak_ptr_factory_(this) { |
| ZX_DEBUG_ASSERT(gatt_dispatcher_); |
| ZX_DEBUG_ASSERT(client_); |
| |
| client_->SetNotificationHandler(fit::bind_member(this, &RemoteServiceManager::OnNotification)); |
| } |
| |
| RemoteServiceManager::~RemoteServiceManager() { |
| ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid()); |
| |
| client_->SetNotificationHandler({}); |
| ClearServices(); |
| |
| // Resolve all pending requests with an error. |
| att::Status status(HostError::kFailed); |
| |
| auto pending = std::move(pending_); |
| while (!pending.empty()) { |
| // This copies |services|. |
| pending.front().Complete(status, services_); |
| pending.pop(); |
| } |
| } |
| |
| void RemoteServiceManager::Initialize(att::StatusCallback cb, std::vector<UUID> services) { |
| ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid()); |
| |
| auto self = weak_ptr_factory_.GetWeakPtr(); |
| |
| auto init_cb = [self, user_init_cb = std::move(cb)](att::Status status) { |
| TRACE_DURATION("bluetooth", "gatt::RemoteServiceManager::Initialize::init_cb"); |
| if (!self) |
| return; |
| |
| self->initialized_ = true; |
| |
| user_init_cb(status); |
| |
| // Notify pending ListService() requests. |
| while (!self->pending_.empty()) { |
| self->pending_.front().Complete(status, self->services_); |
| self->pending_.pop(); |
| } |
| }; |
| |
| // Start out with the MTU exchange. |
| client_->ExchangeMTU([self, init_cb = std::move(init_cb), services = std::move(services)]( |
| att::Status status, uint16_t mtu) mutable { |
| if (!self) { |
| init_cb(att::Status(HostError::kFailed)); |
| return; |
| } |
| |
| if (bt_is_error(status, TRACE, "gatt", "MTU exchange failed")) { |
| init_cb(status); |
| return; |
| } |
| |
| Client::ServiceCallback svc_cb = [self](const ServiceData& service_data) { |
| if (!self) { |
| return; |
| } |
| |
| att::Handle handle = service_data.range_start; |
| auto iter = self->services_.find(handle); |
| if (iter != self->services_.end()) { |
| bt_log(ERROR, "gatt", "found duplicate service attribute handle! (%#.4x)", handle); |
| return; |
| } |
| |
| auto svc = fbl::AdoptRef( |
| new RemoteService(service_data, self->client_->AsWeakPtr(), self->gatt_dispatcher_)); |
| if (!svc) { |
| bt_log(DEBUG, "gatt", "failed to allocate RemoteService"); |
| return; |
| } |
| |
| self->services_[handle] = svc; |
| }; |
| |
| auto status_cb = [self, init_cb = std::move(init_cb)](att::Status status) { |
| TRACE_DURATION("bluetooth", "gatt::RemoteServiceManager::Initialize::MTUCallback::status_cb"); |
| if (!self) { |
| init_cb(att::Status(HostError::kFailed)); |
| return; |
| } |
| |
| // Service discovery support is mandatory for servers (v5.0, Vol 3, |
| // Part G, 4.2). |
| if (bt_is_error(status, TRACE, "gatt", "failed to discover services")) { |
| // Clear services that were buffered so far. |
| self->ClearServices(); |
| } else if (self->svc_watcher_) { |
| // Notify all discovered services here. |
| for (auto& iter : self->services_) { |
| TRACE_DURATION("bluetooth", "gatt::RemoteServiceManager::svc_watcher_"); |
| self->svc_watcher_(iter.second); |
| } |
| } |
| |
| init_cb(status); |
| }; |
| |
| auto primary_discov_cb = [self, services, status_cb = std::move(status_cb), |
| svc_cb = svc_cb.share()](att::Status status) mutable { |
| if (!self || !status) { |
| status_cb(status); |
| return; |
| } |
| |
| auto secondary_discov_cb = [cb = std::move(status_cb)](att::Status status) mutable { |
| // Not all GATT servers support the "secondary service" group type. We suppress the |
| // "Unsupported Group Type" error code and simply report no services instead of treating it |
| // as a fatal condition (errors propagated up the stack from here will cause the connection |
| // to be terminated). |
| if (status.is_protocol_error() && |
| status.protocol_error() == att::ErrorCode::kUnsupportedGroupType) { |
| bt_log(DEBUG, "gatt", "peer does not support secondary services; ignoring ATT error"); |
| status = att::Status(); |
| } |
| cb(status); |
| }; |
| self->DiscoverServices(ServiceKind::SECONDARY, std::move(services), std::move(svc_cb), |
| std::move(secondary_discov_cb)); |
| }; |
| |
| self->DiscoverServices(ServiceKind::PRIMARY, std::move(services), std::move(svc_cb), |
| std::move(primary_discov_cb)); |
| }); |
| } |
| |
| void RemoteServiceManager::DiscoverServices(ServiceKind kind, std::vector<UUID> services, |
| Client::ServiceCallback svc_cb, |
| att::StatusCallback status_cb) { |
| if (!services.empty()) { |
| client_->DiscoverServicesWithUuids(kind, std::move(svc_cb), std::move(status_cb), |
| std::move(services)); |
| } else { |
| client_->DiscoverServices(kind, std::move(svc_cb), std::move(status_cb)); |
| } |
| } |
| |
| void RemoteServiceManager::ListServices(const std::vector<UUID>& uuids, |
| ServiceListCallback callback) { |
| ServiceListRequest request(std::move(callback), uuids); |
| if (initialized_) { |
| request.Complete(att::Status(), services_); |
| } else { |
| pending_.push(std::move(request)); |
| } |
| } |
| |
| fbl::RefPtr<RemoteService> RemoteServiceManager::FindService(att::Handle handle) { |
| auto iter = services_.find(handle); |
| return iter == services_.end() ? nullptr : iter->second; |
| } |
| |
| void RemoteServiceManager::ClearServices() { |
| auto services = std::move(services_); |
| for (auto& iter : services) { |
| iter.second->ShutDown(); |
| } |
| } |
| |
| void RemoteServiceManager::OnNotification(bool, att::Handle value_handle, const ByteBuffer& value) { |
| ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid()); |
| |
| if (services_.empty()) { |
| bt_log(DEBUG, "gatt", "ignoring notification from unknown service"); |
| return; |
| } |
| |
| // Find the service that |value_handle| belongs to. |
| auto iter = services_.upper_bound(value_handle); |
| if (iter != services_.begin()) |
| --iter; |
| |
| // If |value_handle| is within the previous service then we found it. |
| auto& svc = iter->second; |
| ZX_DEBUG_ASSERT(value_handle >= svc->handle()); |
| |
| if (svc->info().range_end >= value_handle) { |
| svc->HandleNotification(value_handle, value); |
| } |
| } |
| |
| } // namespace bt::gatt::internal |