xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/sdp/client_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/client.h"
16 
17 #include <pw_async/dispatcher.h>
18 
19 #include <chrono>
20 #include <ratio>
21 
22 #include "pw_bluetooth_sapphire/internal/host/l2cap/fake_channel.h"
23 #include "pw_bluetooth_sapphire/internal/host/l2cap/fake_channel_test.h"
24 #include "pw_bluetooth_sapphire/internal/host/sdp/service_record.h"
25 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
26 #include "pw_unit_test/framework.h"
27 
28 namespace bt::sdp {
29 namespace {
30 
31 using TestingBase = bt::l2cap::testing::FakeChannelTest;
32 constexpr l2cap::ChannelId kTestChannelId = 0x0041;
33 constexpr uint16_t kResponseMaxSize = 672;
34 
35 class ClientTest : public TestingBase {
36  public:
37   ClientTest() = default;
38 
39  protected:
SetUp()40   void SetUp() override {
41     ChannelOptions options(kTestChannelId);
42     options.link_type = bt::LinkType::kACL;
43     channel_ = CreateFakeChannel(options);
44   }
45 
TearDown()46   void TearDown() override { channel_ = nullptr; }
47 
48  private:
49   std::unique_ptr<l2cap::testing::FakeChannel> channel_;
50 };
51 
52 // Flower Path Test:
53 //  - sends correctly formatted request
54 //  - receives response in the callback
55 //  - receives kNotFound at the end of the callbacks
56 //  - closes SDP channel when client is deallocated
TEST_F(ClientTest,ConnectAndQuery)57 TEST_F(ClientTest, ConnectAndQuery) {
58   {
59     auto client = Client::Create(fake_chan()->GetWeakPtr(), dispatcher());
60 
61     EXPECT_TRUE(fake_chan()->activated());
62 
63     size_t cb_count = 0;
64     auto result_cb =
65         [&](fit::result<
66             Error<>,
67             std::reference_wrapper<const std::map<AttributeId, DataElement>>>
68                 attrs_result) {
69           cb_count++;
70           if (cb_count == 3) {
71             EXPECT_EQ(Error(HostError::kNotFound), attrs_result);
72             return true;
73           }
74           const std::map<AttributeId, DataElement>& attrs =
75               attrs_result.value();
76           // All results should have the ServiceClassIdList.
77           EXPECT_EQ(1u, attrs.count(kServiceClassIdList));
78           // The first result has a kProtocolDescriptorList and the second has a
79           // kBluetoothProfileDescriptorList
80           if (cb_count == 1) {
81             EXPECT_EQ(1u, attrs.count(kProtocolDescriptorList));
82             EXPECT_EQ(0u, attrs.count(kBluetoothProfileDescriptorList));
83           } else if (cb_count == 2) {
84             EXPECT_EQ(0u, attrs.count(kProtocolDescriptorList));
85             EXPECT_EQ(1u, attrs.count(kBluetoothProfileDescriptorList));
86           }
87           return true;
88         };
89 
90     const StaticByteBuffer kSearchExpectedParams(
91         // ServiceSearchPattern
92         0x35,
93         0x03,  // Sequence uint8 3 bytes
94         0x19,
95         0x11,
96         0x0B,  // UUID (kAudioSink)
97         0xFF,
98         0xFF,  // MaxAttributeByteCount (no max)
99         // Attribute ID list
100         0x35,
101         0x09,  // Sequence uint8 9 bytes
102         0x09,
103         0x00,
104         0x01,  // uint16_t (kServiceClassIdList)
105         0x09,
106         0x00,
107         0x04,  // uint16_t (kProtocolDescriptorList)
108         0x09,
109         0x00,
110         0x09,  // uint16_t (kBluetoothProfileDescriptorList)
111         0x00   // No continuation state
112     );
113 
114     uint16_t request_tid;
115     bool success = false;
116 
117     fake_chan()->SetSendCallback(
118         [&request_tid, &success, &kSearchExpectedParams](auto packet) {
119           // First byte should be type.
120           ASSERT_LE(3u, packet->size());
121           ASSERT_EQ(kServiceSearchAttributeRequest, (*packet)[0]);
122           ASSERT_EQ(kSearchExpectedParams, packet->view(5));
123           request_tid = ((*packet)[1] << 8) != 0 || (*packet)[2];
124           success = true;
125         },
126         dispatcher());
127 
128     // Search for all A2DP sinks, get the:
129     //  - Service Class ID list
130     //  - Descriptor List
131     //  - Bluetooth Profile Descriptor List
132     client->ServiceSearchAttributes({profile::kAudioSink},
133                                     {kServiceClassIdList,
134                                      kProtocolDescriptorList,
135                                      kBluetoothProfileDescriptorList},
136                                     result_cb);
137     RunUntilIdle();
138     EXPECT_TRUE(success);
139 
140     // Receive the response
141     // Record makes building the response easier.
142     ServiceRecord rec;
143     rec.AddProtocolDescriptor(ServiceRecord::kPrimaryProtocolList,
144                               protocol::kL2CAP,
145                               DataElement(l2cap::kAVDTP));
146     // The second element here indicates version 1.3 (specified in A2DP spec)
147     rec.AddProtocolDescriptor(ServiceRecord::kPrimaryProtocolList,
148                               protocol::kAVDTP,
149                               DataElement(uint16_t{0x0103}));
150     rec.AddProfile(profile::kAudioSink, 1, 3);
151     ServiceSearchAttributeResponse rsp;
152     rsp.SetAttribute(0,
153                      kServiceClassIdList,
154                      DataElement({DataElement(profile::kAudioSink)}));
155     rsp.SetAttribute(0,
156                      kProtocolDescriptorList,
157                      rec.GetAttribute(kProtocolDescriptorList).Clone());
158 
159     rsp.SetAttribute(1,
160                      kServiceClassIdList,
161                      DataElement({DataElement(profile::kAudioSink)}));
162     rsp.SetAttribute(1,
163                      kBluetoothProfileDescriptorList,
164                      rec.GetAttribute(kBluetoothProfileDescriptorList).Clone());
165 
166     auto rsp_ptr = rsp.GetPDU(0xFFFF /* Max attribute bytes */,
167                               request_tid,
168                               kResponseMaxSize,
169                               BufferView());
170     fake_chan()->Receive(*rsp_ptr);
171 
172     RunUntilIdle();
173 
174     EXPECT_EQ(3u, cb_count);
175   }
176   EXPECT_FALSE(fake_chan()->activated());
177 }
178 
TEST_F(ClientTest,TwoQueriesSubsequent)179 TEST_F(ClientTest, TwoQueriesSubsequent) {
180   {
181     auto client = Client::Create(fake_chan()->GetWeakPtr(), dispatcher());
182 
183     EXPECT_TRUE(fake_chan()->activated());
184 
185     size_t cb_count = 0;
186     auto result_cb =
187         [&](fit::result<
188             Error<>,
189             std::reference_wrapper<const std::map<AttributeId, DataElement>>>
190                 attrs_result) {
191           cb_count++;
192           // We return no results for both queries.
193           EXPECT_EQ(Error(HostError::kNotFound), attrs_result);
194           return true;
195         };
196 
197     const StaticByteBuffer kSearchExpectedParams(
198         // ServiceSearchPattern
199         0x35,
200         0x03,  // Sequence uint8 3 bytes
201         0x19,
202         0x11,
203         0x0B,  // UUID (kAudioSink)
204         0xFF,
205         0xFF,  // MaxAttributeByteCount (no max)
206         // Attribute ID list
207         0x35,
208         0x03,  // Sequence uint8 3 bytes
209         0x09,
210         0x00,
211         0x01,  // uint16_t (kServiceClassIdList)
212         0x00   // No continuation state
213     );
214 
215     uint16_t request_tid;
216     bool success = false;
217 
218     fake_chan()->SetSendCallback(
219         [&request_tid, &success, &kSearchExpectedParams](auto packet) {
220           // First byte should be type.
221           ASSERT_LE(3u, packet->size());
222           ASSERT_EQ(kServiceSearchAttributeRequest, (*packet)[0]);
223           ASSERT_EQ(kSearchExpectedParams, packet->view(5));
224           request_tid = ((*packet)[1] << 8) != 0 || (*packet)[2];
225           success = true;
226         },
227         dispatcher());
228 
229     // Search for all A2DP sinks, get the:
230     //  - Service Class ID list
231     client->ServiceSearchAttributes(
232         {profile::kAudioSink}, {kServiceClassIdList}, result_cb);
233     RunUntilIdle();
234     EXPECT_TRUE(success);
235 
236     // Receive the response (empty response)
237     // Record makes building the response easier.
238     ServiceSearchAttributeResponse rsp;
239     auto rsp_ptr = rsp.GetPDU(0xFFFF /* Max attribute bytes */,
240                               request_tid,
241                               kResponseMaxSize,
242                               BufferView());
243     fake_chan()->Receive(*rsp_ptr);
244 
245     RunUntilIdle();
246 
247     EXPECT_EQ(1u, cb_count);
248 
249     // Twice
250     success = false;
251     client->ServiceSearchAttributes(
252         {profile::kAudioSink}, {kServiceClassIdList}, result_cb);
253     RunUntilIdle();
254     EXPECT_TRUE(success);
255 
256     rsp_ptr = rsp.GetPDU(0xFFFF /* Max attribute bytes */,
257                          request_tid,
258                          kResponseMaxSize,
259                          BufferView());
260     fake_chan()->Receive(*rsp_ptr);
261 
262     RunUntilIdle();
263 
264     EXPECT_EQ(2u, cb_count);
265   }
266   EXPECT_FALSE(fake_chan()->activated());
267 }
268 
TEST_F(ClientTest,TwoQueriesQueued)269 TEST_F(ClientTest, TwoQueriesQueued) {
270   {
271     auto client = Client::Create(fake_chan()->GetWeakPtr(), dispatcher());
272 
273     EXPECT_TRUE(fake_chan()->activated());
274 
275     size_t cb_count = 0;
276     auto result_cb =
277         [&](fit::result<
278             Error<>,
279             std::reference_wrapper<const std::map<AttributeId, DataElement>>>
280                 attrs_result) {
281           cb_count++;
282           // We return no results for both queries.
283           EXPECT_EQ(Error(HostError::kNotFound), attrs_result);
284           return true;
285         };
286 
287     const StaticByteBuffer kSearchExpectedParams(
288         // ServiceSearchPattern
289         0x35,
290         0x03,  // Sequence uint8 3 bytes
291         0x19,
292         0x11,
293         0x0B,  // UUID (kAudioSink)
294         0xFF,
295         0xFF,  // MaxAttributeByteCount (no max)
296         // Attribute ID list
297         0x35,
298         0x03,  // Sequence uint8 3 bytes
299         0x09,
300         0x00,
301         0x01,  // uint16_t (kServiceClassIdList)
302         0x00   // No continuation state
303     );
304 
305     uint16_t request_tid;
306     size_t sent_packets = 0;
307 
308     fake_chan()->SetSendCallback(
309         [&request_tid, &sent_packets, &kSearchExpectedParams](auto packet) {
310           // First byte should be type.
311           ASSERT_LE(3u, packet->size());
312           ASSERT_EQ(kServiceSearchAttributeRequest, (*packet)[0]);
313           ASSERT_EQ(kSearchExpectedParams, packet->view(5));
314           request_tid = ((*packet)[1] << 8) != 0 || (*packet)[2];
315           sent_packets++;
316         },
317         dispatcher());
318 
319     // Search for all A2DP sinks, get the:
320     //  - Service Class ID list
321     client->ServiceSearchAttributes(
322         {profile::kAudioSink}, {kServiceClassIdList}, result_cb);
323     // Twice (without waiting)
324     client->ServiceSearchAttributes(
325         {profile::kAudioSink}, {kServiceClassIdList}, result_cb);
326     RunUntilIdle();
327     // Only one request should have been sent.
328     EXPECT_EQ(1u, sent_packets);
329 
330     // Receive the response (empty response)
331     // Record makes building the response easier.
332     ServiceSearchAttributeResponse rsp;
333     auto rsp_ptr = rsp.GetPDU(0xFFFF /* Max attribute bytes */,
334                               request_tid,
335                               kResponseMaxSize,
336                               BufferView());
337     fake_chan()->Receive(*rsp_ptr);
338 
339     RunUntilIdle();
340 
341     EXPECT_EQ(1u, cb_count);
342     // The second request should have been sent when the first completed.
343     EXPECT_EQ(2u, sent_packets);
344 
345     // Respond to the second request.
346     rsp_ptr = rsp.GetPDU(0xFFFF /* Max attribute bytes */,
347                          request_tid,
348                          kResponseMaxSize,
349                          BufferView());
350     fake_chan()->Receive(*rsp_ptr);
351 
352     RunUntilIdle();
353 
354     EXPECT_EQ(2u, cb_count);
355     EXPECT_EQ(2u, sent_packets);
356   }
357   EXPECT_FALSE(fake_chan()->activated());
358 }
359 
360 // Continuing response test:
361 //  - send correctly formatted request
362 //  - receives a response with a continuing response
363 //  - sends a second request to get the rest of the response
364 //  - receives the continued response
365 //  - responds with the results
366 //  - gives up when callback returns false
TEST_F(ClientTest,ContinuingResponseRequested)367 TEST_F(ClientTest, ContinuingResponseRequested) {
368   auto client = Client::Create(fake_chan()->GetWeakPtr(), dispatcher());
369 
370   size_t cb_count = 0;
371   auto result_cb =
372       [&](fit::result<
373           Error<>,
374           std::reference_wrapper<const std::map<AttributeId, DataElement>>>
375               attrs_result) {
376         cb_count++;
377         if (cb_count == 3) {
378           EXPECT_EQ(Error(HostError::kNotFound), attrs_result);
379           return true;
380         }
381         const std::map<AttributeId, DataElement>& attrs = attrs_result.value();
382         // All results should have the ServiceClassIdList.
383         EXPECT_EQ(1u, attrs.count(kServiceClassIdList));
384         EXPECT_EQ(1u, attrs.count(kProtocolDescriptorList));
385         return true;
386       };
387 
388   const StaticByteBuffer kSearchExpectedParams(
389       // ServiceSearchPattern
390       0x35,
391       0x03,  // Sequence uint8 3 bytes
392       0x19,
393       0x11,
394       0x0B,  // UUID (kAudioSink)
395       0xFF,
396       0xFF,  // MaxAttributeByteCount (no max)
397       // Attribute ID list
398       0x35,
399       0x06,  // Sequence uint8 6 bytes
400       0x09,
401       0x00,
402       0x01,  // uint16_t (0x0001 = kServiceClassIdList)
403       0x09,
404       0x00,
405       0x04  // uint16_t (0x0004 = kProtocolDescriptorList)
406   );
407 
408   size_t requests_made = 0;
409 
410   // Record makes building the response easier.
411   ServiceRecord rec;
412   rec.AddProtocolDescriptor(ServiceRecord::kPrimaryProtocolList,
413                             protocol::kL2CAP,
414                             DataElement(l2cap::kAVDTP));
415   // The second element here indicates version 1.3 (specified in A2DP spec)
416   rec.AddProtocolDescriptor(ServiceRecord::kPrimaryProtocolList,
417                             protocol::kAVDTP,
418                             DataElement(uint16_t{0x0103}));
419   rec.AddProfile(profile::kAudioSink, 1, 3);
420   ServiceSearchAttributeResponse rsp;
421   rsp.SetAttribute(
422       0, kServiceClassIdList, DataElement({DataElement(profile::kAudioSink)}));
423   rsp.SetAttribute(0,
424                    kProtocolDescriptorList,
425                    rec.GetAttribute(kProtocolDescriptorList).Clone());
426   rsp.SetAttribute(
427       1, kServiceClassIdList, DataElement({DataElement(profile::kAudioSink)}));
428   rsp.SetAttribute(1,
429                    kProtocolDescriptorList,
430                    rec.GetAttribute(kProtocolDescriptorList).Clone());
431 
432   fake_chan()->SetSendCallback(
433       [&](auto packet) {
434         requests_made++;
435         // First byte should be type.
436         ASSERT_LE(5u, packet->size());
437         ASSERT_EQ(kServiceSearchAttributeRequest, (*packet)[0]);
438         uint16_t request_tid = ((*packet)[1] << 8) != 0 || (*packet)[2];
439         ASSERT_EQ(kSearchExpectedParams,
440                   packet->view(5, kSearchExpectedParams.size()));
441         // The stuff after the params is the continuation state.
442         auto rsp_ptr =
443             rsp.GetPDU(16 /* Max attribute bytes */,
444                        request_tid,
445                        kResponseMaxSize,
446                        packet->view(5 + kSearchExpectedParams.size() + 1));
447         fake_chan()->Receive(*rsp_ptr);
448       },
449       dispatcher());
450 
451   // Search for all A2DP sinks, get the:
452   //  - Service Class ID list
453   //  - Descriptor List
454   //  - Bluetooth Profile Descriptor List
455   client->ServiceSearchAttributes(
456       {profile::kAudioSink},
457       {kServiceClassIdList, kProtocolDescriptorList},
458       result_cb);
459   RunUntilIdle();
460   EXPECT_EQ(3u, cb_count);
461   EXPECT_EQ(4u, requests_made);
462 }
463 
464 // No results test:
465 //  - send correctly formatted request
466 //  - receives response with no results
467 //  - callback with no results (kNotFound right away)
TEST_F(ClientTest,NoResults)468 TEST_F(ClientTest, NoResults) {
469   auto client = Client::Create(fake_chan()->GetWeakPtr(), dispatcher());
470 
471   size_t cb_count = 0;
472   auto result_cb =
473       [&](fit::result<
474           Error<>,
475           std::reference_wrapper<const std::map<AttributeId, DataElement>>>
476               attrs_result) {
477         cb_count++;
478         EXPECT_EQ(Error(HostError::kNotFound), attrs_result);
479         return true;
480       };
481 
482   const StaticByteBuffer kSearchExpectedParams(
483       // ServiceSearchPattern
484       0x35,
485       0x03,  // Sequence uint8 3 bytes
486       0x19,
487       0x11,
488       0x0B,  // UUID (kAudioSink)
489       0xFF,
490       0xFF,  // MaxAttributeByteCount (no max)
491       // Attribute ID list
492       0x35,
493       0x06,  // Sequence uint8 6 bytes
494       0x09,
495       0x00,
496       0x01,  // uint16_t (0x0001 = kServiceClassIdList)
497       0x09,
498       0x00,
499       0x04,  // uint16_t (0x0004 = kProtocolDescriptorList)
500       0x00   // No continuation state
501   );
502 
503   uint16_t request_tid;
504   bool success = false;
505 
506   fake_chan()->SetSendCallback(
507       [&request_tid, &success, &kSearchExpectedParams](auto packet) {
508         // First byte should be type.
509         ASSERT_LE(3u, packet->size());
510         ASSERT_EQ(kServiceSearchAttributeRequest, (*packet)[0]);
511         ASSERT_EQ(kSearchExpectedParams, packet->view(5));
512         request_tid = ((*packet)[1] << 8) != 0 || (*packet)[2];
513         success = true;
514       },
515       dispatcher());
516 
517   // Search for all A2DP sinks, get the:
518   //  - Service Class ID list
519   //  - Descriptor List
520   //  - Bluetooth Profile Descriptor List
521   client->ServiceSearchAttributes(
522       {profile::kAudioSink},
523       {kServiceClassIdList, kProtocolDescriptorList},
524       result_cb);
525   RunUntilIdle();
526   EXPECT_TRUE(success);
527 
528   // Receive an empty response
529   ServiceSearchAttributeResponse rsp;
530   auto rsp_ptr = rsp.GetPDU(0xFFFF /* Max attribute bytes */,
531                             request_tid,
532                             kResponseMaxSize,
533                             BufferView());
534   fake_chan()->Receive(*rsp_ptr);
535 
536   RunUntilIdle();
537 
538   EXPECT_EQ(1u, cb_count);
539 }
540 
541 // Disconnect early test:
542 //  - send request
543 //  - remote end disconnects
544 //  - result should be called with kLinkDisconnected
TEST_F(ClientTest,Disconnected)545 TEST_F(ClientTest, Disconnected) {
546   auto client = Client::Create(fake_chan()->GetWeakPtr(), dispatcher());
547 
548   size_t cb_count = 0;
549   auto result_cb =
550       [&](fit::result<
551           Error<>,
552           std::reference_wrapper<const std::map<AttributeId, DataElement>>>
553               attrs_result) {
554         cb_count++;
555         EXPECT_EQ(Error(HostError::kLinkDisconnected), attrs_result);
556         return true;
557       };
558 
559   const StaticByteBuffer kSearchExpectedParams(
560       // ServiceSearchPattern
561       0x35,
562       0x03,  // Sequence uint8 3 bytes
563       0x19,
564       0x11,
565       0x0B,  // UUID (kAudioSink)
566       0xFF,
567       0xFF,  // MaxAttributeByteCount (no max)
568       // Attribute ID list
569       0x35,
570       0x06,  // Sequence uint8 6 bytes
571       0x09,
572       0x00,
573       0x01,  // uint16_t (0x0001 = kServiceClassIdList)
574       0x09,
575       0x00,
576       0x04,  // uint16_t (0x0004 = kProtocolDescriptorList)
577       0x00   // No continuation state
578   );
579 
580   bool requested = false;
581 
582   fake_chan()->SetSendCallback(
583       [&](auto packet) {
584         // First byte should be type.
585         ASSERT_LE(3u, packet->size());
586         ASSERT_EQ(kServiceSearchAttributeRequest, (*packet)[0]);
587         ASSERT_EQ(kSearchExpectedParams, packet->view(5));
588         requested = true;
589       },
590       dispatcher());
591 
592   // Search for all A2DP sinks, get the:
593   //  - Service Class ID list
594   //  - Descriptor List
595   //  - Bluetooth Profile Descriptor List
596   client->ServiceSearchAttributes(
597       {profile::kAudioSink},
598       {kServiceClassIdList, kProtocolDescriptorList},
599       result_cb);
600   RunUntilIdle();
601   EXPECT_TRUE(requested);
602   EXPECT_EQ(0u, cb_count);
603 
604   // Remote end closes the channel.
605   fake_chan()->Close();
606 
607   RunUntilIdle();
608 
609   EXPECT_EQ(1u, cb_count);
610 }
611 
612 // Malformed reply test:
613 //  - remote end sends wrong packet type in response (dropped)
614 //  - remote end sends invalid response
615 //  - callback receives no response with a malformed packet error
TEST_F(ClientTest,InvalidResponse)616 TEST_F(ClientTest, InvalidResponse) {
617   auto client = Client::Create(fake_chan()->GetWeakPtr(), dispatcher());
618 
619   size_t cb_count = 0;
620   auto result_cb =
621       [&](fit::result<
622           Error<>,
623           std::reference_wrapper<const std::map<AttributeId, DataElement>>>
624               attrs_result) {
625         cb_count++;
626         EXPECT_EQ(Error(HostError::kPacketMalformed), attrs_result);
627         return true;
628       };
629 
630   const StaticByteBuffer kSearchExpectedParams(
631       // ServiceSearchPattern
632       0x35,
633       0x03,  // Sequence uint8 3 bytes
634       0x19,
635       0x11,
636       0x0B,  // UUID (kAudioSink)
637       0xFF,
638       0xFF,  // MaxAttributeByteCount (no max)
639       // Attribute ID list
640       0x35,
641       0x06,  // Sequence uint8 6 bytes
642       0x09,
643       0x00,
644       0x01,  // uint16_t (0x0001 = kServiceClassIdList)
645       0x09,
646       0x00,
647       0x04,  // uint16_t (0x0004 = kProtocolDescriptorList)
648       0x00   // No continuation state
649   );
650 
651   uint16_t request_tid;
652   bool requested = false;
653 
654   fake_chan()->SetSendCallback(
655       [&](auto packet) {
656         // First byte should be type.
657         ASSERT_LE(3u, packet->size());
658         ASSERT_EQ(kServiceSearchAttributeRequest, (*packet)[0]);
659         ASSERT_EQ(kSearchExpectedParams, packet->view(5));
660         request_tid = ((*packet)[1] << 8) != 0 || (*packet)[2];
661         requested = true;
662       },
663       dispatcher());
664 
665   // Search for all A2DP sinks, get the:
666   //  - Service Class ID list
667   //  - Descriptor List
668   //  - Bluetooth Profile Descriptor List
669   client->ServiceSearchAttributes(
670       {profile::kAudioSink},
671       {kServiceClassIdList, kProtocolDescriptorList},
672       result_cb);
673   RunUntilIdle();
674   EXPECT_TRUE(requested);
675   EXPECT_EQ(0u, cb_count);
676 
677   // Remote end sends some unparsable stuff for the packet.
678   fake_chan()->Receive(StaticByteBuffer(0x07,
679                                         UpperBits(request_tid),
680                                         LowerBits(request_tid),
681                                         0x00,
682                                         0x03,
683                                         0x05,
684                                         0x06,
685                                         0x07));
686 
687   RunUntilIdle();
688 
689   EXPECT_EQ(1u, cb_count);
690 }
691 
692 // Time out (or possibly dropped packets that were malformed)
TEST_F(ClientTest,Timeout)693 TEST_F(ClientTest, Timeout) {
694   constexpr uint32_t kTimeoutMs = 10000;
695   auto client = Client::Create(fake_chan()->GetWeakPtr(), dispatcher());
696 
697   size_t cb_count = 0;
698   auto result_cb =
699       [&](fit::result<
700           Error<>,
701           std::reference_wrapper<const std::map<AttributeId, DataElement>>>
702               attrs_result) {
703         cb_count++;
704         EXPECT_EQ(Error(HostError::kTimedOut), attrs_result);
705         return true;
706       };
707 
708   const StaticByteBuffer kSearchExpectedParams(
709       // ServiceSearchPattern
710       0x35,
711       0x03,  // Sequence uint8 3 bytes
712       0x19,
713       0x11,
714       0x0B,  // UUID (kAudioSink)
715       0xFF,
716       0xFF,  // MaxAttributeByteCount (no max)
717       // Attribute ID list
718       0x35,
719       0x06,  // Sequence uint8 6 bytes
720       0x09,
721       0x00,
722       0x01,  // uint16_t (0x0001 = kServiceClassIdList)
723       0x09,
724       0x00,
725       0x04,  // uint16_t (0x0004 = kProtocolDescriptorList)
726       0x00   // No continuation state
727   );
728 
729   bool requested = false;
730 
731   fake_chan()->SetSendCallback(
732       [&](auto packet) {
733         // First byte should be type.
734         ASSERT_LE(3u, packet->size());
735         ASSERT_EQ(kServiceSearchAttributeRequest, (*packet)[0]);
736         ASSERT_EQ(kSearchExpectedParams, packet->view(5));
737         requested = true;
738       },
739       dispatcher());
740 
741   // Search for all A2DP sinks, get the:
742   //  - Service Class ID list
743   //  - Descriptor List
744   //  - Bluetooth Profile Descriptor List
745   client->ServiceSearchAttributes(
746       {profile::kAudioSink},
747       {kServiceClassIdList, kProtocolDescriptorList},
748       result_cb);
749   RunUntilIdle();
750   EXPECT_TRUE(requested);
751   EXPECT_EQ(0u, cb_count);
752 
753   // Wait until the timeout happens
754   RunFor(std::chrono::milliseconds(kTimeoutMs + 1));
755 
756   EXPECT_EQ(1u, cb_count);
757 }
758 
TEST_F(ClientTest,DestroyClientInErrorResultCallbackDoesNotCrash)759 TEST_F(ClientTest, DestroyClientInErrorResultCallbackDoesNotCrash) {
760   constexpr uint32_t kTimeoutMs = 10000;
761   auto client = Client::Create(fake_chan()->GetWeakPtr(), dispatcher());
762 
763   size_t cb_count = 0;
764   auto result_cb =
765       [&](fit::result<
766           Error<>,
767           std::reference_wrapper<const std::map<AttributeId, DataElement>>>
768               attrs_result) {
769         cb_count++;
770         EXPECT_TRUE(attrs_result.is_error());
771         client.reset();
772         return true;
773       };
774 
775   bool requested = false;
776   fake_chan()->SetSendCallback([&](auto /*packet*/) { requested = true; },
777                                dispatcher());
778 
779   client->ServiceSearchAttributes(
780       {profile::kAudioSink},
781       {kServiceClassIdList, kProtocolDescriptorList},
782       result_cb);
783   RunUntilIdle();
784   EXPECT_TRUE(requested);
785   EXPECT_EQ(0u, cb_count);
786 
787   // Wait until the timeout happens
788   RunFor(std::chrono::milliseconds(kTimeoutMs + 1));
789 
790   EXPECT_EQ(1u, cb_count);
791 }
792 
TEST_F(ClientTest,DestroyClientInDisconnectedResultCallback)793 TEST_F(ClientTest, DestroyClientInDisconnectedResultCallback) {
794   auto client = Client::Create(fake_chan()->GetWeakPtr(), dispatcher());
795 
796   size_t cb_count = 0;
797   auto result_cb =
798       [&](fit::result<
799           Error<>,
800           std::reference_wrapper<const std::map<AttributeId, DataElement>>>
801               attrs_result) {
802         cb_count++;
803         EXPECT_EQ(Error(HostError::kLinkDisconnected), attrs_result);
804         client.reset();
805         return true;
806       };
807 
808   bool requested = false;
809   fake_chan()->SetSendCallback([&](auto /*packet*/) { requested = true; },
810                                dispatcher());
811 
812   client->ServiceSearchAttributes(
813       {profile::kAudioSink},
814       {kServiceClassIdList, kProtocolDescriptorList},
815       result_cb);
816   RunUntilIdle();
817   EXPECT_TRUE(requested);
818   EXPECT_EQ(0u, cb_count);
819 
820   // Remote end closes the channel.
821   fake_chan()->Close();
822 
823   RunUntilIdle();
824 
825   EXPECT_EQ(1u, cb_count);
826 }
827 
828 }  // namespace
829 }  // namespace bt::sdp
830