| // 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 |