xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/att/attribute.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_bluetooth_sapphire/internal/host/att/attribute.h"
16 
17 namespace bt::att {
18 
AccessRequirements()19 AccessRequirements::AccessRequirements() : value_(0u), min_enc_key_size_(0u) {}
20 
AccessRequirements(bool encryption,bool authentication,bool authorization,uint8_t min_enc_key_size)21 AccessRequirements::AccessRequirements(bool encryption,
22                                        bool authentication,
23                                        bool authorization,
24                                        uint8_t min_enc_key_size)
25     : value_(kAttributePermissionBitAllowed),
26       min_enc_key_size_(min_enc_key_size) {
27   if (encryption) {
28     value_ |= kAttributePermissionBitEncryptionRequired;
29   }
30   if (authentication) {
31     value_ |= kAttributePermissionBitAuthenticationRequired;
32   }
33   if (authorization) {
34     value_ |= kAttributePermissionBitAuthorizationRequired;
35   }
36 }
37 
Attribute(AttributeGrouping * group,Handle handle,const UUID & type,const AccessRequirements & read_reqs,const AccessRequirements & write_reqs)38 Attribute::Attribute(AttributeGrouping* group,
39                      Handle handle,
40                      const UUID& type,
41                      const AccessRequirements& read_reqs,
42                      const AccessRequirements& write_reqs)
43     : group_(group),
44       handle_(handle),
45       type_(type),
46       read_reqs_(read_reqs),
47       write_reqs_(write_reqs) {
48   PW_DCHECK(group_);
49   PW_DCHECK(is_initialized());
50 }
51 
Attribute()52 Attribute::Attribute() : handle_(kInvalidHandle) {}
53 
SetValue(const ByteBuffer & value)54 void Attribute::SetValue(const ByteBuffer& value) {
55   PW_DCHECK(value.size());
56   PW_DCHECK(value.size() <= kMaxAttributeValueLength);
57   PW_DCHECK(!write_reqs_.allowed());
58   value_ = DynamicByteBuffer(value);
59 }
60 
ReadAsync(PeerId peer_id,uint16_t offset,ReadResultCallback result_callback) const61 bool Attribute::ReadAsync(PeerId peer_id,
62                           uint16_t offset,
63                           ReadResultCallback result_callback) const {
64   if (!is_initialized() || !read_handler_)
65     return false;
66 
67   if (!read_reqs_.allowed())
68     return false;
69 
70   read_handler_(peer_id, handle_, offset, std::move(result_callback));
71   return true;
72 }
73 
WriteAsync(PeerId peer_id,uint16_t offset,const ByteBuffer & value,WriteResultCallback result_callback) const74 bool Attribute::WriteAsync(PeerId peer_id,
75                            uint16_t offset,
76                            const ByteBuffer& value,
77                            WriteResultCallback result_callback) const {
78   if (!is_initialized() || !write_handler_)
79     return false;
80 
81   if (!write_reqs_.allowed())
82     return false;
83 
84   write_handler_(peer_id, handle_, offset, value, std::move(result_callback));
85   return true;
86 }
87 
AttributeGrouping(const UUID & group_type,Handle start_handle,size_t attr_count,const ByteBuffer & decl_value)88 AttributeGrouping::AttributeGrouping(const UUID& group_type,
89                                      Handle start_handle,
90                                      size_t attr_count,
91                                      const ByteBuffer& decl_value)
92     : start_handle_(start_handle), active_(false) {
93   PW_DCHECK(start_handle_ != kInvalidHandle);
94   PW_DCHECK(decl_value.size());
95 
96   // It is a programmer error to provide an attr_count which overflows a handle
97   // - this is why the below static cast is OK.
98   PW_CHECK(kHandleMax - start_handle >= attr_count);
99   auto handle_attr_count = static_cast<Handle>(attr_count);
100 
101   end_handle_ = start_handle + handle_attr_count;
102   attributes_.reserve(handle_attr_count + 1);
103 
104   // TODO(armansito): Allow callers to require at most encryption.
105   attributes_.push_back(Attribute(
106       this,
107       start_handle,
108       group_type,
109       AccessRequirements(/*encryption=*/false,
110                          /*authentication=*/false,
111                          /*authorization=*/false),  // read allowed, no security
112       AccessRequirements()));                       // write disallowed
113 
114   attributes_[0].SetValue(decl_value);
115 }
116 
AddAttribute(const UUID & type,const AccessRequirements & read_reqs,const AccessRequirements & write_reqs)117 Attribute* AttributeGrouping::AddAttribute(
118     const UUID& type,
119     const AccessRequirements& read_reqs,
120     const AccessRequirements& write_reqs) {
121   if (complete())
122     return nullptr;
123 
124   PW_DCHECK(attributes_[attributes_.size() - 1].handle() < end_handle_);
125 
126   // Groupings may not exceed kHandleMax attributes, so if we are incomplete per
127   // the `complete()` check, we necessarily have < kHandleMax attributes. Thus
128   // it is safe to cast attributes_.size() into a Handle.
129   PW_CHECK(attributes_.size() < kHandleMax - start_handle_);
130   Handle handle = start_handle_ + static_cast<Handle>(attributes_.size());
131   attributes_.push_back(Attribute(this, handle, type, read_reqs, write_reqs));
132 
133   return &attributes_[handle - start_handle_];
134 }
135 
136 }  // namespace bt::att
137