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 "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
18 #include "pw_bluetooth_sapphire/internal/host/sdp/data_element.h"
19 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
20 #include "pw_unit_test/framework.h"
21
22 namespace bt::sdp {
23 namespace {
24
25 // Test: making a new record generates a UUID.
26 // Test: GetAttribute
27 // Test: HasAttribute
28 // - returns true if attribute is there
29 // - returns false if attribute is not there
30 // Test: RemoveAttribute
31 // Test: SetServiceClassUUIDs
32 // - Sets the right attribute with the right format.
TEST(ServiceRecordTest,BasicFunctionality)33 TEST(ServiceRecordTest, BasicFunctionality) {
34 ServiceRecord record;
35
36 record.SetHandle(kSDPHandle);
37
38 EXPECT_EQ(kSDPHandle, record.handle());
39
40 EXPECT_TRUE(record.HasAttribute(kServiceId));
41
42 EXPECT_FALSE(record.HasAttribute(kServiceClassIdList));
43
44 // This isn't a valid service class ID list:
45 // - ServiceDiscoveryServerServiceClassID
46 // - BrowseGroupDescriptorServiceClassID
47 UUID sdp_id(uint16_t{0x1000});
48 UUID group_id(uint16_t{0x1001});
49 std::vector<UUID> service_class;
50 service_class.push_back(sdp_id);
51 service_class.emplace_back(group_id);
52
53 record.SetServiceClassUUIDs(service_class);
54
55 EXPECT_TRUE(record.HasAttribute(kServiceClassIdList));
56 EXPECT_FALSE(record.IsRegisterable());
57
58 const DataElement& elem = record.GetAttribute(kServiceClassIdList);
59
60 EXPECT_EQ(DataElement::Type::kSequence, elem.type());
61
62 std::optional<std::vector<DataElement>> vec =
63 elem.Get<std::vector<DataElement>>();
64
65 EXPECT_TRUE(vec);
66 EXPECT_EQ(2u, vec->size());
67 EXPECT_EQ(sdp_id, *(vec->at(0).Get<UUID>()));
68 EXPECT_EQ(group_id, *(vec->at(1).Get<UUID>()));
69
70 record.RemoveAttribute(kServiceId);
71
72 EXPECT_FALSE(record.HasAttribute(kServiceId));
73 }
74
75 // Test: GetAttributesInRange
76 // - Returns any attributes that are present.
TEST(ServiceRecordTest,GetAttributesInRange)77 TEST(ServiceRecordTest, GetAttributesInRange) {
78 ServiceRecord record;
79
80 record.SetHandle(kSDPHandle);
81
82 record.SetAttribute(0xf00d, DataElement());
83 record.SetAttribute(0x0001, DataElement());
84 record.SetAttribute(0xfeed, DataElement());
85
86 auto attrs =
87 record.GetAttributesInRange(kServiceRecordHandle, kServiceRecordHandle);
88
89 EXPECT_EQ(1u, attrs.size());
90 EXPECT_EQ(kServiceRecordHandle, *attrs.begin());
91
92 // Get a copy of all elements
93 attrs = record.GetAttributesInRange(0, 0xFFFF);
94
95 EXPECT_EQ(5u, attrs.size()); // kServiceRecord, kServiceId, three added above
96 EXPECT_NE(attrs.end(), attrs.find(kServiceRecordHandle));
97 EXPECT_NE(attrs.end(), attrs.find(kServiceId));
98 EXPECT_NE(attrs.end(), attrs.find(0xf00d));
99 EXPECT_NE(attrs.end(), attrs.find(0x0001));
100 EXPECT_NE(attrs.end(), attrs.find(0xfeed));
101 }
102
103 // Test: FindUUID
104 // - Only returns true if all uuids are present
TEST(ServiceRecordTest,FindUUID)105 TEST(ServiceRecordTest, FindUUID) {
106 ServiceRecord record;
107
108 DataElement elem;
109 elem.Set(UUID(uint16_t{0xfeaa}));
110 record.SetAttribute(0xb001, std::move(elem));
111 elem.Set(UUID(uint16_t{0xfeed}));
112 record.SetAttribute(0xb002, std::move(elem));
113 elem.Set(UUID(uint16_t{0xfeec}));
114 record.SetAttribute(0xb003, std::move(elem));
115
116 std::unordered_set<UUID> search_pattern;
117 search_pattern.insert(UUID(uint16_t{0xfeaa}));
118
119 EXPECT_TRUE(record.FindUUID(search_pattern));
120
121 search_pattern.insert(UUID(uint16_t{0xfeec}));
122
123 EXPECT_TRUE(record.FindUUID(search_pattern));
124
125 search_pattern.insert(UUID(uint16_t{0xfeeb}));
126
127 EXPECT_FALSE(record.FindUUID(search_pattern));
128 }
129
130 // Test: AddProtocolDescriptor
TEST(ServiceRecordTest,AddProtocolDescriptor)131 TEST(ServiceRecordTest, AddProtocolDescriptor) {
132 ServiceRecord record;
133
134 EXPECT_FALSE(record.HasAttribute(kProtocolDescriptorList));
135
136 DataElement psm(uint16_t{0x0001}); // SDP PSM
137
138 record.AddProtocolDescriptor(
139 ServiceRecord::kPrimaryProtocolList, protocol::kL2CAP, std::move(psm));
140
141 // clang-format off
142 StaticByteBuffer expected(
143 0x35, 0x08, // Data Element Sequence (8 bytes)
144 0x35, 0x06, // Data Element Sequence (6 bytes)
145 0x19, // UUID (16 bits)
146 0x01, 0x00, // L2CAP protocol UUID
147 0x09, // uint16_t
148 0x00, 0x01 // PSM=SDP
149 );
150 // clang-format on
151
152 EXPECT_TRUE(record.HasAttribute(kProtocolDescriptorList));
153
154 const DataElement& val = record.GetAttribute(kProtocolDescriptorList);
155 DynamicByteBuffer block(val.WriteSize());
156 val.Write(&block);
157
158 EXPECT_EQ(expected.size(), block.size());
159 EXPECT_TRUE(ContainersEqual(expected, block));
160
161 record.AddProtocolDescriptor(
162 ServiceRecord::kPrimaryProtocolList, protocol::kSDP, DataElement());
163
164 EXPECT_TRUE(record.HasAttribute(kProtocolDescriptorList));
165
166 // clang-format off
167 StaticByteBuffer expected_sdp(
168 0x35, 0x0D, // Data Element Sequence (13 bytes)
169 0x35, 0x06, // Data Element Sequence (6 bytes)
170 0x19, // UUID (16 bits)
171 0x01, 0x00, // L2CAP protocol UUID
172 0x09, // uint16_t
173 0x00, 0x01, // PSM=SDP
174 0x35, 0x03, // Data Element Sequence (3 bytes)
175 0x19, // UUID (16 bits)
176 0x00, 0x01 // kSDP protocol
177 );
178 // clang-format on
179
180 const DataElement& pdl = record.GetAttribute(kProtocolDescriptorList);
181 DynamicByteBuffer block_sdp(pdl.WriteSize());
182 pdl.Write(&block_sdp);
183
184 EXPECT_EQ(expected_sdp.size(), block_sdp.size());
185 EXPECT_TRUE(ContainersEqual(expected_sdp, block_sdp));
186
187 record.AddProtocolDescriptor(1, protocol::kRFCOMM, DataElement());
188
189 EXPECT_TRUE(record.HasAttribute(kAdditionalProtocolDescriptorList));
190
191 // clang-format off
192 StaticByteBuffer expected_addl(
193 0x35, 0x07, // Data Element Sequence (AdditionalProtocolDescriptorLists)
194 0x35, 0x05, // Data Element Sequence (ProtocolDescriptorList 1)
195 0x35, 0x03, // Data Element Sequence Protocol List 1 Descriptor 0
196 0x19, // UUID (16 bits)
197 0x00, 0x03 // kRFCOMM protocol
198 );
199 // clang-format on
200
201 const DataElement& apdl =
202 record.GetAttribute(kAdditionalProtocolDescriptorList);
203 DynamicByteBuffer block_addl(apdl.WriteSize());
204 apdl.Write(&block_addl);
205
206 EXPECT_EQ(expected_addl.size(), block_addl.size());
207 EXPECT_TRUE(ContainersEqual(expected_addl, block_addl));
208 }
209
210 // Test: AddProfile
211 // - Adds an attribute if it doesn't exist
212 // - Appends to the attribute if it does exist
TEST(ServiceRecordTest,AddProfile)213 TEST(ServiceRecordTest, AddProfile) {
214 ServiceRecord record;
215
216 EXPECT_FALSE(record.HasAttribute(kBluetoothProfileDescriptorList));
217
218 record.AddProfile(profile::kSerialPort, 2, 3);
219
220 EXPECT_TRUE(record.HasAttribute(kBluetoothProfileDescriptorList));
221
222 // clang-format off
223 StaticByteBuffer expected(
224 0x35, 0x08, // Data Element Sequence (8 bytes)
225 0x35, 0x06, // Data Element Sequence (6 bytes)
226 0x19, // UUID (16 bits)
227 0x11, 0x01, // SerialPort protocol UUID
228 0x09, // uint16_t
229 0x02, 0x03 // 16 bit profile version number (major=2, minor=3)
230 );
231 // clang-format on
232
233 const DataElement& val = record.GetAttribute(kBluetoothProfileDescriptorList);
234 DynamicByteBuffer block(val.WriteSize());
235 val.Write(&block);
236
237 EXPECT_EQ(expected.size(), block.size());
238 EXPECT_TRUE(ContainersEqual(expected, block));
239
240 record.AddProfile(profile::kDialupNetworking, 4, 5);
241
242 // clang-format off
243 StaticByteBuffer expected_dun(
244 0x35, 0x10, // Data Element Sequence (16 bytes)
245 0x35, 0x06, // Data Element Sequence (6 bytes)
246 0x19, // UUID (16 bits)
247 0x11, 0x01, // SerialPort protocol UUID
248 0x09, // uint16_t
249 0x02, 0x03, // 16 bit profile version number (major=2, minor=3)
250 0x35, 0x06, // Data Element Sequence (6 bytes)
251 0x19, // UUID (16 bits)
252 0x11, 0x03, // DUN UUID
253 0x09, // uint16_t
254 0x04, 0x05 // 16 bit profile version number (major=4, minor=5)
255 );
256 // clang-format on
257
258 const DataElement& val_dun =
259 record.GetAttribute(kBluetoothProfileDescriptorList);
260 DynamicByteBuffer block_dun(val_dun.WriteSize());
261 val_dun.Write(&block_dun);
262
263 EXPECT_EQ(expected_dun.size(), block_dun.size());
264 EXPECT_TRUE(ContainersEqual(expected_dun, block_dun));
265 }
266
267 // Test: AddInfo, GetInfo
268 // - Requires at least one is set.
269 // - Adds the right attributes to a set.
270 // - Returns the right attributes when retrieved.
TEST(ServiceRecordTest,AddAndGetInfo)271 TEST(ServiceRecordTest, AddAndGetInfo) {
272 ServiceRecord record;
273
274 EXPECT_FALSE(record.HasAttribute(kLanguageBaseAttributeIdList));
275 EXPECT_TRUE(record.GetInfo().empty());
276
277 // Can't add with nothing specified.
278 EXPECT_FALSE(record.AddInfo("", "", "", ""));
279 EXPECT_FALSE(record.HasAttribute(kLanguageBaseAttributeIdList));
280 // Can't add with only language code set.
281 EXPECT_FALSE(record.AddInfo("en", "", "", ""));
282 EXPECT_FALSE(record.HasAttribute(kLanguageBaseAttributeIdList));
283 // Can't add with an invalid language code.
284 EXPECT_FALSE(record.AddInfo("english", "foo", "bar", "baz"));
285 EXPECT_FALSE(record.HasAttribute(kLanguageBaseAttributeIdList));
286
287 // Can add valid information.
288 EXPECT_TRUE(record.AddInfo("en", "SDP", "", ""));
289 EXPECT_TRUE(record.HasAttribute(kLanguageBaseAttributeIdList));
290 // Can manually get the information from the base attribute ID list.
291 const DataElement& val = record.GetAttribute(kLanguageBaseAttributeIdList);
292
293 auto triplets = val.Get<std::vector<DataElement>>();
294 EXPECT_TRUE(triplets);
295 // They have to be triplets in this.
296 EXPECT_TRUE(triplets->size() % 3 == 0);
297 EXPECT_EQ(DataElement::Type::kUnsignedInt, triplets->at(0).type());
298 EXPECT_EQ(DataElement::Type::kUnsignedInt, triplets->at(1).type());
299 EXPECT_EQ(DataElement::Type::kUnsignedInt, triplets->at(2).type());
300 auto lang = triplets->at(0).Get<uint16_t>();
301 EXPECT_TRUE(lang);
302 EXPECT_EQ(0x656e, *lang); // should be 'en' in ascii (but big-endian)
303
304 auto encoding = triplets->at(1).Get<uint16_t>();
305 EXPECT_TRUE(encoding);
306 EXPECT_EQ(106, *encoding); // should always be UTF-8
307
308 auto base_attrid = triplets->at(2).Get<uint16_t>();
309 EXPECT_TRUE(base_attrid);
310 EXPECT_EQ(0x0100, *base_attrid); // The primary language must be at 0x0100.
311
312 EXPECT_TRUE(record.HasAttribute(*base_attrid + kServiceNameOffset));
313 const DataElement& name_elem =
314 record.GetAttribute(*base_attrid + kServiceNameOffset);
315 auto name = name_elem.Get<std::string>();
316 EXPECT_TRUE(name);
317 EXPECT_EQ("SDP", *name);
318
319 EXPECT_TRUE(record.HasAttribute(*base_attrid + kServiceDescriptionOffset));
320 const DataElement& desc_elem =
321 record.GetAttribute(*base_attrid + kServiceDescriptionOffset);
322 auto desc = desc_elem.Get<std::string>();
323 EXPECT_TRUE(desc);
324 EXPECT_EQ("", *desc);
325
326 EXPECT_FALSE(record.HasAttribute(*base_attrid + kProviderNameOffset));
327
328 // Can add a second set of information.
329 EXPECT_TRUE(record.AddInfo("fr", "French SDP", "", "Foo"));
330 // Should be able to retrieve both sets of info.
331 auto infos = record.GetInfo();
332 EXPECT_EQ(infos.size(), 2u);
333 EXPECT_EQ(infos.at(0).language_code, "en");
334 EXPECT_EQ(infos.at(0).name.value(), "SDP");
335 EXPECT_EQ(infos.at(0).description.value(), "");
336 EXPECT_FALSE(infos.at(0).provider);
337 EXPECT_EQ(infos.at(1).language_code, "fr");
338 EXPECT_EQ(infos.at(1).name.value(), "French SDP");
339 EXPECT_FALSE(infos.at(1).description);
340 EXPECT_EQ(infos.at(1).provider.value(), "Foo");
341 }
342
343 // Test: IsRegisterable
344 // - ServiceRecord must contain the BrowseGroupList to be registerable.
TEST(ServiceRecordTest,IsRegisterable)345 TEST(ServiceRecordTest, IsRegisterable) {
346 ServiceRecord record;
347 record.SetServiceClassUUIDs({profile::kAVRemoteControlTarget});
348 record.AddProtocolDescriptor(ServiceRecord::kPrimaryProtocolList,
349 protocol::kL2CAP,
350 DataElement(uint16_t{25}));
351 record.AddProtocolDescriptor(1, protocol::kL2CAP, DataElement(uint16_t{27}));
352
353 EXPECT_FALSE(record.IsRegisterable());
354
355 UUID browse_uuid = UUID(uint16_t{0xfeaa});
356 std::vector<DataElement> browse_list;
357 browse_list.emplace_back(DataElement(browse_uuid));
358 record.SetAttribute(kBrowseGroupList, DataElement(std::move(browse_list)));
359
360 EXPECT_TRUE(record.IsRegisterable());
361 }
362
363 // Test: ToString
364 // - The ToString() method correctly formats the fields.
TEST(ServiceRecordTest,ToString)365 TEST(ServiceRecordTest, ToString) {
366 ServiceRecord record;
367 EXPECT_EQ("", record.ToString());
368
369 record.AddProfile(profile::kAdvancedAudioDistribution, 1, 3);
370
371 EXPECT_EQ(
372 "Profile Descriptor: Sequence { Sequence { "
373 "UUID(0000110d-0000-1000-8000-00805f9b34fb) "
374 "UnsignedInt:2(259) } }\n",
375 record.ToString());
376
377 record.SetServiceClassUUIDs({profile::kAVRemoteControlTarget});
378 EXPECT_EQ(
379 "Profile Descriptor: Sequence { Sequence { "
380 "UUID(0000110d-0000-1000-8000-00805f9b34fb) "
381 "UnsignedInt:2(259) } }\nService Class Id List: Sequence { "
382 "UUID(0000110c-0000-1000-8000-00805f9b34fb) }",
383 record.ToString());
384 }
385
386 } // namespace
387 } // namespace bt::sdp
388