| // 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_remote_service_server.h" |
| |
| #include "helpers.h" |
| |
| using fuchsia::bluetooth::ErrorCode; |
| using fuchsia::bluetooth::Status; |
| using fuchsia::bluetooth::gatt::Characteristic; |
| using fuchsia::bluetooth::gatt::CharacteristicPtr; |
| using fuchsia::bluetooth::gatt::Descriptor; |
| |
| using bt::ByteBuffer; |
| using bt::MutableBufferView; |
| using bt::gatt::CharacteristicData; |
| using bt::gatt::CharacteristicHandle; |
| using bt::gatt::DescriptorData; |
| using bt::gatt::DescriptorHandle; |
| |
| namespace bthost { |
| namespace { |
| |
| // We mask away the "extended properties" property. We expose extended |
| // properties in the same bitfield. |
| constexpr uint8_t kPropertyMask = 0x7F; |
| |
| Characteristic CharacteristicToFidl(const CharacteristicData& characteristic, |
| const std::map<DescriptorHandle, DescriptorData>& descriptors) { |
| Characteristic fidl_char; |
| fidl_char.id = static_cast<uint64_t>(characteristic.value_handle); |
| fidl_char.type = characteristic.type.ToString(); |
| fidl_char.properties = static_cast<uint16_t>(characteristic.properties & kPropertyMask); |
| fidl_char.descriptors.emplace(); // initialize an empty vector |
| |
| // TODO(armansito): Add extended properties. |
| |
| for (const auto& [id, descr] : descriptors) { |
| Descriptor fidl_descr; |
| fidl_descr.id = static_cast<uint64_t>(id.value); |
| fidl_descr.type = descr.type.ToString(); |
| fidl_char.descriptors->push_back(std::move(fidl_descr)); |
| } |
| |
| return fidl_char; |
| } |
| |
| void NopStatusCallback(bt::att::Status) {} |
| |
| } // namespace |
| |
| GattRemoteServiceServer::GattRemoteServiceServer( |
| fbl::RefPtr<bt::gatt::RemoteService> service, fbl::RefPtr<bt::gatt::GATT> gatt, |
| fidl::InterfaceRequest<fuchsia::bluetooth::gatt::RemoteService> request) |
| : GattServerBase(gatt, this, std::move(request)), |
| service_(std::move(service)), |
| weak_ptr_factory_(this) { |
| ZX_DEBUG_ASSERT(service_); |
| } |
| |
| GattRemoteServiceServer::~GattRemoteServiceServer() { |
| for (const auto& iter : notify_handlers_) { |
| if (iter.second != bt::gatt::kInvalidId) { |
| service_->DisableNotifications(iter.first, iter.second, NopStatusCallback); |
| } |
| } |
| } |
| |
| void GattRemoteServiceServer::DiscoverCharacteristics(DiscoverCharacteristicsCallback callback) { |
| auto res_cb = [callback = std::move(callback)](bt::att::Status status, const auto& chrcs) { |
| std::vector<Characteristic> fidl_chrcs; |
| if (status) { |
| for (const auto& [id, chrc] : chrcs) { |
| auto& [chr, descs] = chrc; |
| fidl_chrcs.push_back(CharacteristicToFidl(chr, descs)); |
| } |
| } |
| |
| callback(fidl_helpers::StatusToFidlDeprecated(status, ""), std::move(fidl_chrcs)); |
| }; |
| |
| service_->DiscoverCharacteristics(std::move(res_cb)); |
| } |
| |
| void GattRemoteServiceServer::ReadCharacteristic(uint64_t id, ReadCharacteristicCallback callback) { |
| auto cb = [callback = std::move(callback)](bt::att::Status status, const bt::ByteBuffer& value) { |
| // We always reply with a non-null value. |
| std::vector<uint8_t> vec; |
| |
| if (status && value.size()) { |
| vec.resize(value.size()); |
| |
| MutableBufferView vec_view(vec.data(), vec.size()); |
| value.Copy(&vec_view); |
| } |
| |
| callback(fidl_helpers::StatusToFidlDeprecated(status), std::move(vec)); |
| }; |
| |
| service_->ReadCharacteristic(CharacteristicHandle(id), std::move(cb)); |
| } |
| |
| void GattRemoteServiceServer::ReadLongCharacteristic(uint64_t id, uint16_t offset, |
| uint16_t max_bytes, |
| ReadLongCharacteristicCallback callback) { |
| auto cb = [callback = std::move(callback)](bt::att::Status status, const bt::ByteBuffer& value) { |
| // We always reply with a non-null value. |
| std::vector<uint8_t> vec; |
| |
| if (status && value.size()) { |
| vec.resize(value.size()); |
| |
| MutableBufferView vec_view(vec.data(), vec.size()); |
| value.Copy(&vec_view); |
| } |
| |
| callback(fidl_helpers::StatusToFidlDeprecated(status), std::move(vec)); |
| }; |
| |
| service_->ReadLongCharacteristic(CharacteristicHandle(id), offset, max_bytes, std::move(cb)); |
| } |
| |
| void GattRemoteServiceServer::WriteCharacteristic(uint64_t id, ::std::vector<uint8_t> value, |
| WriteCharacteristicCallback callback) { |
| auto cb = [callback = std::move(callback)](bt::att::Status status) { |
| callback(fidl_helpers::StatusToFidlDeprecated(status, "")); |
| }; |
| |
| service_->WriteCharacteristic(CharacteristicHandle(id), std::move(value), std::move(cb)); |
| } |
| |
| void GattRemoteServiceServer::WriteLongCharacteristic(uint64_t id, uint16_t offset, |
| ::std::vector<uint8_t> value, |
| WriteLongCharacteristicCallback callback) { |
| auto cb = [callback = std::move(callback)](bt::att::Status status) { |
| callback(fidl_helpers::StatusToFidlDeprecated(status, "")); |
| }; |
| |
| service_->WriteLongCharacteristic(CharacteristicHandle(id), offset, std::move(value), |
| std::move(cb)); |
| } |
| |
| void GattRemoteServiceServer::WriteCharacteristicWithoutResponse(uint64_t id, |
| ::std::vector<uint8_t> value) { |
| service_->WriteCharacteristicWithoutResponse(CharacteristicHandle(id), std::move(value)); |
| } |
| |
| void GattRemoteServiceServer::ReadDescriptor(uint64_t id, ReadDescriptorCallback callback) { |
| auto cb = [callback = std::move(callback)](bt::att::Status status, const bt::ByteBuffer& value) { |
| // We always reply with a non-null value. |
| std::vector<uint8_t> vec; |
| |
| if (status && value.size()) { |
| vec.resize(value.size()); |
| |
| MutableBufferView vec_view(vec.data(), vec.size()); |
| value.Copy(&vec_view); |
| } |
| |
| callback(fidl_helpers::StatusToFidlDeprecated(status), std::move(vec)); |
| }; |
| |
| service_->ReadDescriptor(DescriptorHandle(id), std::move(cb)); |
| } |
| |
| void GattRemoteServiceServer::ReadLongDescriptor(uint64_t id, uint16_t offset, uint16_t max_bytes, |
| ReadLongDescriptorCallback callback) { |
| auto cb = [callback = std::move(callback)](bt::att::Status status, const bt::ByteBuffer& value) { |
| // We always reply with a non-null value. |
| std::vector<uint8_t> vec; |
| |
| if (status && value.size()) { |
| vec.resize(value.size()); |
| |
| MutableBufferView vec_view(vec.data(), vec.size()); |
| value.Copy(&vec_view); |
| } |
| |
| callback(fidl_helpers::StatusToFidlDeprecated(status), std::move(vec)); |
| }; |
| |
| service_->ReadLongDescriptor(DescriptorHandle(id), offset, max_bytes, std::move(cb)); |
| } |
| |
| void GattRemoteServiceServer::WriteDescriptor(uint64_t id, ::std::vector<uint8_t> value, |
| WriteDescriptorCallback callback) { |
| service_->WriteDescriptor(DescriptorHandle(id), std::move(value), |
| [callback = std::move(callback)](bt::att::Status status) { |
| callback(fidl_helpers::StatusToFidlDeprecated(status, "")); |
| }); |
| } |
| |
| void GattRemoteServiceServer::WriteLongDescriptor(uint64_t id, uint16_t offset, |
| ::std::vector<uint8_t> value, |
| WriteLongDescriptorCallback callback) { |
| service_->WriteLongDescriptor(DescriptorHandle(id), offset, std::move(value), |
| [callback = std::move(callback)](bt::att::Status status) { |
| callback(fidl_helpers::StatusToFidlDeprecated(status, "")); |
| }); |
| } |
| |
| void GattRemoteServiceServer::NotifyCharacteristic(uint64_t id, bool enable, |
| NotifyCharacteristicCallback callback) { |
| auto handle = CharacteristicHandle(id); |
| if (!enable) { |
| auto iter = notify_handlers_.find(handle); |
| if (iter == notify_handlers_.end()) { |
| callback(fidl_helpers::NewFidlError(ErrorCode::NOT_FOUND, "characteristic not notifying")); |
| return; |
| } |
| |
| if (iter->second == bt::gatt::kInvalidId) { |
| callback(fidl_helpers::NewFidlError(ErrorCode::IN_PROGRESS, |
| "characteristic notification registration pending")); |
| return; |
| } |
| |
| service_->DisableNotifications(handle, iter->second, |
| [callback = std::move(callback)](bt::att::Status status) { |
| callback(fidl_helpers::StatusToFidlDeprecated(status, "")); |
| }); |
| notify_handlers_.erase(iter); |
| |
| return; |
| } |
| |
| if (notify_handlers_.count(handle) != 0) { |
| callback(fidl_helpers::NewFidlError(ErrorCode::ALREADY, "characteristic already notifying")); |
| return; |
| } |
| |
| // Prevent any races and leaks by marking a notification is in progress |
| notify_handlers_[handle] = bt::gatt::kInvalidId; |
| |
| auto self = weak_ptr_factory_.GetWeakPtr(); |
| auto value_cb = [self, id](const ByteBuffer& value) { |
| if (!self) |
| return; |
| |
| self->binding()->events().OnCharacteristicValueUpdated(id, value.ToVector()); |
| }; |
| |
| auto status_cb = [self, svc = service_, handle, callback = std::move(callback)]( |
| bt::att::Status status, HandlerId handler_id) { |
| if (!self) { |
| if (status) { |
| // Disable this handler so it doesn't leak. |
| svc->DisableNotifications(handle, handler_id, NopStatusCallback); |
| } |
| |
| callback(fidl_helpers::NewFidlError(ErrorCode::FAILED, "canceled")); |
| return; |
| } |
| |
| if (status) { |
| ZX_DEBUG_ASSERT(handler_id != bt::gatt::kInvalidId); |
| ZX_DEBUG_ASSERT(self->notify_handlers_.count(handle) == 1u); |
| ZX_DEBUG_ASSERT(self->notify_handlers_[handle] == bt::gatt::kInvalidId); |
| self->notify_handlers_[handle] = handler_id; |
| } else { |
| // Remove our handle holder. |
| self->notify_handlers_.erase(handle); |
| } |
| |
| callback(fidl_helpers::StatusToFidlDeprecated(status, "")); |
| }; |
| |
| service_->EnableNotifications(handle, std::move(value_cb), std::move(status_cb)); |
| } |
| |
| } // namespace bthost |