xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/sdp/service_record_test.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/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