blob: 509042921ad8ed7d11e823e202c2d1ad372a368d [file] [log] [blame]
// Copyright 2020 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 "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/generic_access_client.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/gap.h"
namespace bt::gap::internal {
GenericAccessClient::GenericAccessClient(PeerId peer_id,
gatt::RemoteService::WeakPtr service)
: WeakSelf(this), service_(std::move(service)), peer_id_(peer_id) {
BT_ASSERT(service_.is_alive());
BT_ASSERT(service_->uuid() == kGenericAccessService);
}
void GenericAccessClient::ReadDeviceName(DeviceNameCallback callback) {
service_->DiscoverCharacteristics(
[self = GetWeakPtr(), cb = std::move(callback)](
att::Result<> result, const gatt::CharacteristicMap& chars) mutable {
if (!self.is_alive()) {
return;
}
if (result.is_error()) {
cb(result.take_error());
return;
}
std::optional<gatt::CharacteristicHandle> device_name_value_handle;
for (auto& [handle, chr] : chars) {
auto& data = chr.first;
if (data.type == kDeviceNameCharacteristic) {
device_name_value_handle.emplace(data.value_handle);
break;
}
}
if (!device_name_value_handle) {
bt_log(DEBUG,
"gap-le",
"GAP service does not have device name characteristic "
"(peer: %s)",
bt_str(self->peer_id_));
cb(ToResult(HostError::kNotFound).take_error());
return;
}
// according to Core Spec v5.3, Vol 3, Part C, 12.1: "0 to 248 octets in
// length"
self->service_->ReadLongCharacteristic(
*device_name_value_handle,
/*offset=*/0,
att::kMaxAttributeValueLength,
[self, cb = std::move(cb)](att::Result<> result,
const ByteBuffer& buffer,
bool /*maybe_truncated*/) mutable {
if (!self.is_alive()) {
return;
}
if (bt_is_error(
result,
DEBUG,
"gap-le",
"error reading device name characteristic (peer: %s)",
bt_str(self->peer_id_))) {
cb(result.take_error());
return;
}
const auto device_name_end =
std::find(buffer.begin(), buffer.end(), '\0');
cb(fit::ok(std::string(buffer.begin(), device_name_end)));
});
});
}
void GenericAccessClient::ReadAppearance(AppearanceCallback callback) {
service_->DiscoverCharacteristics([self = GetWeakPtr(),
cb = std::move(callback)](
att::Result<> result,
const gatt::CharacteristicMap&
chars) mutable {
if (!self.is_alive()) {
return;
}
if (result.is_error()) {
cb(result.take_error());
return;
}
std::optional<gatt::CharacteristicHandle> appearance_value_handle;
for (auto& [handle, chr] : chars) {
auto& data = chr.first;
if (data.type == kAppearanceCharacteristic) {
appearance_value_handle.emplace(data.value_handle);
break;
}
}
if (!appearance_value_handle) {
bt_log(DEBUG,
"gap-le",
"GAP service does not have appearance characteristic "
"(peer: %s)",
bt_str(self->peer_id_));
cb(ToResult(HostError::kNotFound).take_error());
return;
}
// according to Core Spec v5.3, Vol 3, Part C, 12.2: "2 octets in length"
self->service_->ReadCharacteristic(
*appearance_value_handle,
[self, cb = std::move(cb)](att::Result<> result,
const ByteBuffer& buffer,
bool /*maybe_truncated*/) mutable {
if (!self.is_alive()) {
return;
}
if (bt_is_error(result,
DEBUG,
"gap-le",
"error reading appearance characteristic (peer: %s)",
bt_str(self->peer_id_))) {
cb(result.take_error());
return;
}
if (buffer.size() != sizeof(uint16_t)) {
bt_log(
DEBUG,
"gap-le",
"appearance characteristic has invalid value size (peer: %s)",
bt_str(self->peer_id_));
cb(ToResult(HostError::kPacketMalformed).take_error());
return;
}
uint16_t char_value = le16toh(buffer.template To<uint16_t>());
cb(fit::ok(char_value));
});
});
}
void GenericAccessClient::ReadPeripheralPreferredConnectionParameters(
ConnectionParametersCallback callback) {
service_->DiscoverCharacteristics([self = GetWeakPtr(),
cb = std::move(callback)](
att::Result<> result,
const gatt::CharacteristicMap&
chars) mutable {
if (!self.is_alive()) {
return;
}
if (result.is_error()) {
cb(result.take_error());
return;
}
std::optional<gatt::CharacteristicHandle> conn_params_value_handle;
for (auto& [handle, chr] : chars) {
auto& data = chr.first;
if (data.type == kPeripheralPreferredConnectionParametersCharacteristic) {
conn_params_value_handle.emplace(data.value_handle);
break;
}
}
if (!conn_params_value_handle) {
bt_log(DEBUG,
"gap-le",
"GAP service does not have peripheral preferred connection "
"parameters characteristic "
"(peer: %s)",
bt_str(self->peer_id_));
cb(ToResult(HostError::kNotFound).take_error());
return;
}
self->service_->ReadCharacteristic(
*conn_params_value_handle,
[self, cb = std::move(cb)](att::Result<> result,
const ByteBuffer& buffer,
bool /*maybe_truncated*/) mutable {
if (!self.is_alive()) {
return;
}
if (bt_is_error(result,
DEBUG,
"gap-le",
"error reading peripheral preferred connection "
"parameters characteristic "
"(peer: %s)",
bt_str(self->peer_id_))) {
cb(result.take_error());
return;
}
if (buffer.size() !=
sizeof(
PeripheralPreferredConnectionParametersCharacteristicValue)) {
bt_log(DEBUG,
"gap-le",
"peripheral preferred connection parameters characteristic "
"has invalid value size "
"(peer: %s)",
bt_str(self->peer_id_));
cb(ToResult(HostError::kPacketMalformed).take_error());
return;
}
auto char_value = buffer.template To<
PeripheralPreferredConnectionParametersCharacteristicValue>();
hci_spec::LEPreferredConnectionParameters params(
le16toh(char_value.min_interval),
le16toh(char_value.max_interval),
le16toh(char_value.max_latency),
le16toh(char_value.supervision_timeout));
cb(fit::ok(params));
});
});
}
} // namespace bt::gap::internal