blob: f78eeaab2a0630020090895193689b0c2ed9c7ca [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_remote_service_server.h"
#include <zircon/errors.h>
#include <algorithm>
#include "helpers.h"
#include "src/connectivity/bluetooth/core/bt-host/att/att.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.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 fuchsia::bluetooth::gatt::WriteOptions;
using bt::ByteBuffer;
using bt::MutableBufferView;
using bt::gatt::CharacteristicData;
using bt::gatt::CharacteristicHandle;
using bt::gatt::DescriptorData;
using bt::gatt::DescriptorHandle;
using bthost::fidl_helpers::CharacteristicHandleFromFidl;
using bthost::fidl_helpers::DescriptorHandleFromFidl;
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, fxl::WeakPtr<bt::gatt::GATT> gatt,
bt::PeerId peer_id, fidl::InterfaceRequest<fuchsia::bluetooth::gatt::RemoteService> request)
: GattServerBase(gatt, this, std::move(request)),
service_(std::move(service)),
peer_id_(peer_id),
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));
};
// TODO(fxbug.dev/63438): The 64 bit `id` can overflow the 16 bits of a bt::att:Handle. Fix this.
service_->ReadCharacteristic(CharacteristicHandleFromFidl(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));
};
// TODO(fxbug.dev/63438): The 64 bit `id` can overflow the 16 bits of a bt::att:Handle. Fix this.
service_->ReadLongCharacteristic(CharacteristicHandleFromFidl(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, ""));
};
// TODO(fxbug.dev/63438): The 64 bit `id` can overflow the 16 bits of a bt::att:Handle. Fix this.
service_->WriteCharacteristic(CharacteristicHandleFromFidl(id), std::move(value), std::move(cb));
}
void GattRemoteServiceServer::WriteLongCharacteristic(uint64_t id, uint16_t offset,
::std::vector<uint8_t> value,
WriteOptions write_options,
WriteLongCharacteristicCallback callback) {
auto cb = [callback = std::move(callback)](bt::att::Status status) {
callback(fidl_helpers::StatusToFidlDeprecated(status, ""));
};
auto reliable_mode = fidl_helpers::ReliableModeFromFidl(write_options);
// TODO(fxbug.dev/63438): The 64 bit `id` can overflow the 16 bits of a bt::att:Handle. Fix this.
service_->WriteLongCharacteristic(CharacteristicHandleFromFidl(id), offset, std::move(value),
std::move(reliable_mode), std::move(cb));
}
void GattRemoteServiceServer::WriteCharacteristicWithoutResponse(uint64_t id,
::std::vector<uint8_t> value) {
// TODO(fxbug.dev/63438): The 64 bit `id` can overflow the 16 bits of a bt::att:Handle. Fix this.
service_->WriteCharacteristicWithoutResponse(CharacteristicHandleFromFidl(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));
};
// TODO(fxbug.dev/63438): The 64 bit `id` can overflow the 16 bits of a bt::att:Handle. Fix this.
service_->ReadDescriptor(DescriptorHandleFromFidl(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));
};
// TODO(fxbug.dev/63438): The 64 bit `id` can overflow the 16 bits of a bt::att:Handle. Fix this.
service_->ReadLongDescriptor(DescriptorHandleFromFidl(id), offset, max_bytes, std::move(cb));
}
void GattRemoteServiceServer::WriteDescriptor(uint64_t id, ::std::vector<uint8_t> value,
WriteDescriptorCallback callback) {
// TODO(fxbug.dev/63438): The 64 bit `id` can overflow the 16 bits of a bt::att:Handle. Fix this.
service_->WriteDescriptor(DescriptorHandleFromFidl(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) {
// TODO(fxbug.dev/63438): The 64 bit `id` can overflow the 16 bits of a bt::att:Handle. Fix this.
service_->WriteLongDescriptor(DescriptorHandleFromFidl(id), offset, std::move(value),
[callback = std::move(callback)](bt::att::Status status) {
callback(fidl_helpers::StatusToFidlDeprecated(status, ""));
});
}
void GattRemoteServiceServer::ReadByType(fuchsia::bluetooth::Uuid uuid,
ReadByTypeCallback callback) {
service_->ReadByType(
fidl_helpers::UuidFromFidl(uuid),
[self = weak_ptr_factory_.GetWeakPtr(), cb = std::move(callback), func = __FUNCTION__](
bt::att::Status status, std::vector<bt::gatt::RemoteService::ReadByTypeResult> results) {
if (!self) {
return;
}
switch (status.error()) {
case bt::HostError::kNoError:
break;
case bt::HostError::kInvalidParameters:
bt_log(WARN, "fidl",
"%s: called with invalid parameters, closing FIDL channel (peer: %s)", func,
bt_str(self->peer_id_));
self->binding()->Close(ZX_ERR_INVALID_ARGS);
return;
default:
cb(fit::error(fidl_helpers::GattStatusToFidl(status)));
return;
}
if (results.size() > fuchsia::bluetooth::gatt::MAX_READ_BY_TYPE_RESULTS) {
cb(fit::error(fuchsia::bluetooth::gatt::Error::TOO_MANY_RESULTS));
return;
}
std::vector<fuchsia::bluetooth::gatt::ReadByTypeResult> fidl_results;
fidl_results.reserve(results.size());
for (const auto& result : results) {
fuchsia::bluetooth::gatt::ReadByTypeResult fidl_result;
fidl_result.set_id(static_cast<uint64_t>(result.handle.value));
if (result.result.is_ok()) {
fidl_result.set_value(result.result.value()->ToVector());
} else {
fidl_result.set_error(
fidl_helpers::GattStatusToFidl(bt::att::Status(result.result.error())));
}
fidl_results.push_back(std::move(fidl_result));
}
cb(fit::ok(std::move(fidl_results)));
});
}
void GattRemoteServiceServer::NotifyCharacteristic(uint64_t id, bool enable,
NotifyCharacteristicCallback callback) {
// TODO(fxbug.dev/63438): The 64 bit `id` can overflow the 16 bits of a bt::att:Handle. Fix this.
auto handle = CharacteristicHandleFromFidl(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