• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/sdp/service_record.h"
16 
17 #include <endian.h>
18 
19 #include <iterator>
20 #include <set>
21 
22 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
23 
24 namespace bt::sdp {
25 
26 namespace {
27 
28 // Adds all UUIDs that it finds in |elem| to |out|, recursing through
29 // sequences and alternatives if necessary.
AddAllUUIDs(const DataElement & elem,std::unordered_set<UUID> * out)30 void AddAllUUIDs(const DataElement& elem, std::unordered_set<UUID>* out) {
31   DataElement::Type type = elem.type();
32   if (type == DataElement::Type::kUuid) {
33     out->emplace(*elem.Get<UUID>());
34   } else if (type == DataElement::Type::kSequence ||
35              type == DataElement::Type::kAlternative) {
36     const DataElement* it;
37     for (size_t idx = 0; nullptr != (it = elem.At(idx)); idx++) {
38       AddAllUUIDs(*it, out);
39     }
40   }
41 }
42 
43 }  // namespace
44 
ServiceRecord()45 ServiceRecord::ServiceRecord() {
46   SetAttribute(kServiceId, DataElement(UUID::Generate()));
47 }
48 
SetAttribute(AttributeId id,DataElement value)49 void ServiceRecord::SetAttribute(AttributeId id, DataElement value) {
50   attributes_.erase(id);
51   attributes_.emplace(id, std::move(value));
52 }
53 
GetAttribute(AttributeId id) const54 const DataElement& ServiceRecord::GetAttribute(AttributeId id) const {
55   auto it = attributes_.find(id);
56   BT_DEBUG_ASSERT_MSG(it != attributes_.end(), "attribute %#.4x not set!", id);
57   return it->second;
58 }
59 
HasAttribute(AttributeId id) const60 bool ServiceRecord::HasAttribute(AttributeId id) const {
61   return attributes_.count(id) == 1;
62 }
63 
RemoveAttribute(AttributeId id)64 void ServiceRecord::RemoveAttribute(AttributeId id) { attributes_.erase(id); }
65 
IsProtocolOnly() const66 bool ServiceRecord::IsProtocolOnly() const {
67   // Protocol-only services have exactly:
68   //  - A UUID (generated by constructor)
69   //  - A primary protocol descriptor
70   //  - A service record handle (assigned by the SDP server)
71   if (attributes_.size() != 3) {
72     return false;
73   }
74   for (AttributeId x :
75        {kServiceRecordHandle, kProtocolDescriptorList, kServiceId}) {
76     if (attributes_.count(x) != 1) {
77       return false;
78     }
79   }
80   return true;
81 }
82 
IsRegisterable() const83 bool ServiceRecord::IsRegisterable() const {
84   // Services must at least have a ServiceClassIDList (5.0, Vol 3, Part B, 5.1)
85   if (!HasAttribute(kServiceClassIdList)) {
86     bt_log(TRACE, "sdp", "record missing ServiceClass");
87     return false;
88   }
89   // Class ID list is a data element sequence in which each data element is
90   // a UUID representing the service classes that a given service record
91   // conforms to. (5.0, Vol 3, Part B, 5.1.2)
92   const DataElement& class_id_list = GetAttribute(kServiceClassIdList);
93   if (class_id_list.type() != DataElement::Type::kSequence) {
94     bt_log(TRACE, "sdp", "class ID list isn't a sequence");
95     return false;
96   }
97 
98   size_t idx;
99   const DataElement* elem;
100   for (idx = 0; nullptr != (elem = class_id_list.At(idx)); idx++) {
101     if (elem->type() != DataElement::Type::kUuid) {
102       bt_log(TRACE, "sdp", "class ID list elements are not all UUIDs");
103       return false;
104     }
105   }
106 
107   if (idx == 0) {
108     bt_log(TRACE, "sdp", "no elements in the Class ID list (need at least 1)");
109     return false;
110   }
111 
112   if (!HasAttribute(kBrowseGroupList)) {
113     bt_log(TRACE, "sdp", "record isn't part of a browse group");
114     return false;
115   }
116 
117   return true;
118 }
119 
SetHandle(ServiceHandle handle)120 void ServiceRecord::SetHandle(ServiceHandle handle) {
121   handle_ = handle;
122   SetAttribute(kServiceRecordHandle, DataElement(uint32_t(handle_)));
123 }
124 
GetAttributesInRange(AttributeId start,AttributeId end) const125 std::set<AttributeId> ServiceRecord::GetAttributesInRange(
126     AttributeId start, AttributeId end) const {
127   std::set<AttributeId> attrs;
128   if (start > end) {
129     return attrs;
130   }
131   for (auto it = attributes_.lower_bound(start);
132        it != attributes_.end() && (it->first <= end);
133        ++it) {
134     attrs.emplace(it->first);
135   }
136 
137   return attrs;
138 }
139 
FindUUID(const std::unordered_set<UUID> & uuids) const140 bool ServiceRecord::FindUUID(const std::unordered_set<UUID>& uuids) const {
141   if (uuids.size() == 0) {
142     return true;
143   }
144   // Gather all the UUIDs in the attributes
145   std::unordered_set<UUID> attribute_uuids;
146   for (const auto& it : attributes_) {
147     AddAllUUIDs(it.second, &attribute_uuids);
148   }
149   for (const auto& uuid : uuids) {
150     if (attribute_uuids.count(uuid) == 0) {
151       return false;
152     }
153   }
154   return true;
155 }
156 
SetServiceClassUUIDs(const std::vector<UUID> & classes)157 void ServiceRecord::SetServiceClassUUIDs(const std::vector<UUID>& classes) {
158   std::vector<DataElement> class_uuids;
159   for (const auto& uuid : classes) {
160     class_uuids.emplace_back(DataElement(uuid));
161   }
162   DataElement class_id_list_val(std::move(class_uuids));
163   SetAttribute(kServiceClassIdList, std::move(class_id_list_val));
164 }
165 
AddProtocolDescriptor(const ProtocolListId id,const UUID & uuid,DataElement params)166 void ServiceRecord::AddProtocolDescriptor(const ProtocolListId id,
167                                           const UUID& uuid,
168                                           DataElement params) {
169   std::vector<DataElement> seq;
170   if (id == kPrimaryProtocolList) {
171     auto list_it = attributes_.find(kProtocolDescriptorList);
172     if (list_it != attributes_.end()) {
173       auto v = list_it->second.Get<std::vector<DataElement>>();
174       seq = std::move(*v);
175     }
176   } else if (addl_protocols_.count(id)) {
177     auto v = addl_protocols_[id].Get<std::vector<DataElement>>();
178     seq = std::move(*v);
179   }
180 
181   std::vector<DataElement> protocol_desc;
182   protocol_desc.emplace_back(DataElement(uuid));
183   if (params.type() == DataElement::Type::kSequence) {
184     auto v = params.Get<std::vector<DataElement>>();
185     auto param_seq = std::move(*v);
186     std::move(std::begin(param_seq),
187               std::end(param_seq),
188               std::back_inserter(protocol_desc));
189   } else if (params.type() != DataElement::Type::kNull) {
190     protocol_desc.emplace_back(std::move(params));
191   }
192 
193   seq.emplace_back(DataElement(std::move(protocol_desc)));
194 
195   if (id == kPrimaryProtocolList) {
196     SetAttribute(kProtocolDescriptorList, DataElement(std::move(seq)));
197   } else {
198     addl_protocols_.erase(id);
199     addl_protocols_.emplace(id, DataElement(std::move(seq)));
200 
201     std::vector<DataElement> addl_protocol_seq;
202     for (const auto& it : addl_protocols_) {
203       addl_protocol_seq.emplace_back(it.second.Clone());
204     }
205 
206     SetAttribute(kAdditionalProtocolDescriptorList,
207                  DataElement(std::move(addl_protocol_seq)));
208   }
209 }
210 
AddProfile(const UUID & uuid,uint8_t major,uint8_t minor)211 void ServiceRecord::AddProfile(const UUID& uuid, uint8_t major, uint8_t minor) {
212   std::vector<DataElement> seq;
213   auto list_it = attributes_.find(kBluetoothProfileDescriptorList);
214   if (list_it != attributes_.end()) {
215     auto v = list_it->second.Get<std::vector<DataElement>>();
216     seq = std::move(*v);
217   }
218 
219   std::vector<DataElement> profile_desc;
220   profile_desc.emplace_back(DataElement(uuid));
221   // Safety notes:
222   // 1.) `<<` applies integer promotion of `major` to `int` (32 bits) before
223   // operating. This makes
224   //     it safe to left shift 8 bits, even though 8 is >= `major`'s original
225   //     width.
226   // 2.) Casting to 16 bits is safe because `major` and `minor` are both only 8
227   // bits, so it is only
228   //     possible for 16 bits of the resulting value to be populated.
229   uint16_t profile_version = static_cast<uint16_t>(
230       (major << std::numeric_limits<uint8_t>::digits) | minor);
231   profile_desc.emplace_back(DataElement(profile_version));
232 
233   seq.emplace_back(DataElement(std::move(profile_desc)));
234 
235   SetAttribute(kBluetoothProfileDescriptorList, DataElement(std::move(seq)));
236 }
237 
AddInfo(const std::string & language_code,const std::string & name,const std::string & description,const std::string & provider)238 bool ServiceRecord::AddInfo(const std::string& language_code,
239                             const std::string& name,
240                             const std::string& description,
241                             const std::string& provider) {
242   if ((name.empty() && description.empty() && provider.empty()) ||
243       (language_code.size() != 2)) {
244     return false;
245   }
246   AttributeId base_attrid = 0x0100;
247   std::vector<DataElement> base_attr_list;
248   auto it = attributes_.find(kLanguageBaseAttributeIdList);
249   if (it != attributes_.end()) {
250     auto v = it->second.Get<std::vector<DataElement>>();
251     base_attr_list = std::move(*v);
252     BT_DEBUG_ASSERT(base_attr_list.size() % 3 == 0);
253     // 0x0100 is guaranteed to be taken, start counting from higher.
254     base_attrid = 0x9000;
255   }
256 
257   // Find the first base_attrid that's not taken
258   while (HasAttribute(base_attrid + kServiceNameOffset) ||
259          HasAttribute(base_attrid + kServiceDescriptionOffset) ||
260          HasAttribute(base_attrid + kProviderNameOffset)) {
261     base_attrid++;
262     if (base_attrid == 0xFFFF) {
263       return false;
264     }
265   }
266 
267   // Core Spec v5.0, Vol 3, Part B, Sect 5.1.8: "The LanguageBaseAttributeIDList
268   // attribute consists of a data element sequence in which each element is a
269   // 16-bit unsigned integer."
270   // The language code consists of two byte characters in left-to-right order,
271   // so it may be considered a 16-bit big-endian integer that can be converted
272   // to host byte order.
273   uint16_t lang_encoded = be16toh(*((const uint16_t*)(language_code.data())));
274   base_attr_list.emplace_back(DataElement(lang_encoded));
275   base_attr_list.emplace_back(DataElement(uint16_t{106}));  // UTF-8
276   base_attr_list.emplace_back(DataElement(base_attrid));
277 
278   if (!name.empty()) {
279     SetAttribute(base_attrid + kServiceNameOffset, DataElement(name));
280   }
281   if (!description.empty()) {
282     SetAttribute(base_attrid + kServiceDescriptionOffset,
283                  DataElement(description));
284   }
285   if (!provider.empty()) {
286     SetAttribute(base_attrid + kProviderNameOffset, DataElement(provider));
287   }
288 
289   SetAttribute(kLanguageBaseAttributeIdList,
290                DataElement(std::move(base_attr_list)));
291   return true;
292 }
293 
ToString() const294 std::string ServiceRecord::ToString() const {
295   std::string str;
296 
297   if (HasAttribute(kBluetoothProfileDescriptorList)) {
298     const DataElement& prof_desc =
299         GetAttribute(kBluetoothProfileDescriptorList);
300     str += "Profile Descriptor: " + prof_desc.ToString() + "\n";
301   }
302 
303   if (HasAttribute(kServiceClassIdList)) {
304     const DataElement& svc_class_list = GetAttribute(kServiceClassIdList);
305     str += "Service Class Id List: " + svc_class_list.ToString();
306   }
307 
308   return str;
309 }
310 
311 }  // namespace bt::sdp
312