• 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 "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
268 //  - Requires at least one is set.
269 //  - Adds the right attributes to a set.
TEST(ServiceRecordTest,AddInfo)270 TEST(ServiceRecordTest, AddInfo) {
271   ServiceRecord record;
272 
273   EXPECT_FALSE(record.HasAttribute(kLanguageBaseAttributeIdList));
274 
275   // Can't add with nothing specified.
276   EXPECT_FALSE(record.AddInfo("en", "", "", ""));
277   EXPECT_FALSE(record.HasAttribute(kLanguageBaseAttributeIdList));
278 
279   EXPECT_TRUE(record.AddInfo("en", "SDP", "��", ""));
280 
281   EXPECT_TRUE(record.HasAttribute(kLanguageBaseAttributeIdList));
282   const DataElement& val = record.GetAttribute(kLanguageBaseAttributeIdList);
283 
284   auto triplets = val.Get<std::vector<DataElement>>();
285   EXPECT_TRUE(triplets);
286   // They have to be triplets in this.
287   EXPECT_TRUE(triplets->size() % 3 == 0);
288   EXPECT_EQ(DataElement::Type::kUnsignedInt, triplets->at(0).type());
289   EXPECT_EQ(DataElement::Type::kUnsignedInt, triplets->at(1).type());
290   EXPECT_EQ(DataElement::Type::kUnsignedInt, triplets->at(2).type());
291   auto lang = triplets->at(0).Get<uint16_t>();
292   EXPECT_TRUE(lang);
293   EXPECT_EQ(0x656e, *lang);  // should be 'en' in ascii (but big-endian)
294 
295   auto encoding = triplets->at(1).Get<uint16_t>();
296   EXPECT_TRUE(encoding);
297   EXPECT_EQ(106, *encoding);  // should always be UTF-8
298 
299   auto base_attrid = triplets->at(2).Get<uint16_t>();
300   EXPECT_TRUE(base_attrid);
301   EXPECT_EQ(0x0100, *base_attrid);  // The primary language must be at 0x0100.
302 
303   EXPECT_TRUE(record.HasAttribute(*base_attrid + kServiceNameOffset));
304   const DataElement& name_elem =
305       record.GetAttribute(*base_attrid + kServiceNameOffset);
306   auto name = name_elem.Get<std::string>();
307   EXPECT_TRUE(name);
308   EXPECT_EQ("SDP", *name);
309 
310   EXPECT_TRUE(record.HasAttribute(*base_attrid + kServiceDescriptionOffset));
311   const DataElement& desc_elem =
312       record.GetAttribute(*base_attrid + kServiceDescriptionOffset);
313   auto desc = desc_elem.Get<std::string>();
314   EXPECT_TRUE(desc);
315   EXPECT_EQ("��", *desc);
316 
317   EXPECT_FALSE(record.HasAttribute(*base_attrid + kProviderNameOffset));
318 }
319 
320 // Test: IsRegisterable
321 // - ServiceRecord must contain the BrowseGroupList to be registerable.
TEST(ServiceRecordTest,IsRegisterable)322 TEST(ServiceRecordTest, IsRegisterable) {
323   ServiceRecord record;
324   record.SetServiceClassUUIDs({profile::kAVRemoteControlTarget});
325   record.AddProtocolDescriptor(ServiceRecord::kPrimaryProtocolList,
326                                protocol::kL2CAP,
327                                DataElement(uint16_t{25}));
328   record.AddProtocolDescriptor(1, protocol::kL2CAP, DataElement(uint16_t{27}));
329 
330   EXPECT_FALSE(record.IsRegisterable());
331 
332   UUID browse_uuid = UUID(uint16_t{0xfeaa});
333   std::vector<DataElement> browse_list;
334   browse_list.emplace_back(DataElement(browse_uuid));
335   record.SetAttribute(kBrowseGroupList, DataElement(std::move(browse_list)));
336 
337   EXPECT_TRUE(record.IsRegisterable());
338 }
339 
340 // Test: ToString
341 // - The ToString() method correctly formats the fields.
TEST(ServiceRecordTest,ToString)342 TEST(ServiceRecordTest, ToString) {
343   ServiceRecord record;
344   EXPECT_EQ("", record.ToString());
345 
346   record.AddProfile(profile::kAdvancedAudioDistribution, 1, 3);
347 
348   EXPECT_EQ(
349       "Profile Descriptor: Sequence { Sequence { "
350       "UUID(0000110d-0000-1000-8000-00805f9b34fb) "
351       "UnsignedInt:2(259) } }\n",
352       record.ToString());
353 
354   record.SetServiceClassUUIDs({profile::kAVRemoteControlTarget});
355   EXPECT_EQ(
356       "Profile Descriptor: Sequence { Sequence { "
357       "UUID(0000110d-0000-1000-8000-00805f9b34fb) "
358       "UnsignedInt:2(259) } }\nService Class Id List: Sequence { "
359       "UUID(0000110c-0000-1000-8000-00805f9b34fb) }",
360       record.ToString());
361 }
362 
363 }  // namespace
364 }  // namespace bt::sdp
365