blob: 3ef68cc017c8ec75b1d40265aa19feaa467a499c [file] [log] [blame]
// Copyright 2023 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/internal/host/att/attribute.h"
namespace bt::att {
AccessRequirements::AccessRequirements() : value_(0u), min_enc_key_size_(0u) {}
AccessRequirements::AccessRequirements(bool encryption,
bool authentication,
bool authorization,
uint8_t min_enc_key_size)
: value_(kAttributePermissionBitAllowed),
min_enc_key_size_(min_enc_key_size) {
if (encryption) {
value_ |= kAttributePermissionBitEncryptionRequired;
}
if (authentication) {
value_ |= kAttributePermissionBitAuthenticationRequired;
}
if (authorization) {
value_ |= kAttributePermissionBitAuthorizationRequired;
}
}
Attribute::Attribute(AttributeGrouping* group,
Handle handle,
const UUID& type,
const AccessRequirements& read_reqs,
const AccessRequirements& write_reqs)
: group_(group),
handle_(handle),
type_(type),
read_reqs_(read_reqs),
write_reqs_(write_reqs) {
BT_DEBUG_ASSERT(group_);
BT_DEBUG_ASSERT(is_initialized());
}
Attribute::Attribute() : handle_(kInvalidHandle) {}
void Attribute::SetValue(const ByteBuffer& value) {
BT_DEBUG_ASSERT(value.size());
BT_DEBUG_ASSERT(value.size() <= kMaxAttributeValueLength);
BT_DEBUG_ASSERT(!write_reqs_.allowed());
value_ = DynamicByteBuffer(value);
}
bool Attribute::ReadAsync(PeerId peer_id,
uint16_t offset,
ReadResultCallback result_callback) const {
if (!is_initialized() || !read_handler_)
return false;
if (!read_reqs_.allowed())
return false;
read_handler_(peer_id, handle_, offset, std::move(result_callback));
return true;
}
bool Attribute::WriteAsync(PeerId peer_id,
uint16_t offset,
const ByteBuffer& value,
WriteResultCallback result_callback) const {
if (!is_initialized() || !write_handler_)
return false;
if (!write_reqs_.allowed())
return false;
write_handler_(peer_id, handle_, offset, value, std::move(result_callback));
return true;
}
AttributeGrouping::AttributeGrouping(const UUID& group_type,
Handle start_handle,
size_t attr_count,
const ByteBuffer& decl_value)
: start_handle_(start_handle), active_(false) {
BT_DEBUG_ASSERT(start_handle_ != kInvalidHandle);
BT_DEBUG_ASSERT(decl_value.size());
// It is a programmer error to provide an attr_count which overflows a handle
// - this is why the below static cast is OK.
BT_ASSERT(kHandleMax - start_handle >= attr_count);
auto handle_attr_count = static_cast<Handle>(attr_count);
end_handle_ = start_handle + handle_attr_count;
attributes_.reserve(handle_attr_count + 1);
// TODO(armansito): Allow callers to require at most encryption.
attributes_.push_back(Attribute(
this,
start_handle,
group_type,
AccessRequirements(/*encryption=*/false,
/*authentication=*/false,
/*authorization=*/false), // read allowed, no security
AccessRequirements())); // write disallowed
attributes_[0].SetValue(decl_value);
}
Attribute* AttributeGrouping::AddAttribute(
const UUID& type,
const AccessRequirements& read_reqs,
const AccessRequirements& write_reqs) {
if (complete())
return nullptr;
BT_DEBUG_ASSERT(attributes_[attributes_.size() - 1].handle() < end_handle_);
// Groupings may not exceed kHandleMax attributes, so if we are incomplete per
// the `complete()` check, we necessarily have < kHandleMax attributes. Thus
// it is safe to cast attributes_.size() into a Handle.
BT_ASSERT(attributes_.size() < kHandleMax - start_handle_);
Handle handle = start_handle_ + static_cast<Handle>(attributes_.size());
attributes_.push_back(Attribute(this, handle, type, read_reqs, write_reqs));
return &attributes_[handle - start_handle_];
}
} // namespace bt::att