• 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/l2cap/a2dp_offload_manager.h"
16 
17 #include <memory>
18 
19 #include "pw_bluetooth_sapphire/internal/host/common/host_error.h"
20 #include "pw_bluetooth_sapphire/internal/host/testing/controller_test.h"
21 #include "pw_bluetooth_sapphire/internal/host/testing/mock_controller.h"
22 #include "pw_bluetooth_sapphire/internal/host/testing/test_packets.h"
23 
24 namespace bt::l2cap {
25 namespace {
26 
27 namespace hci_android = bt::hci_spec::vendor::android;
28 using namespace bt::testing;
29 
30 constexpr hci_spec::ConnectionHandle kTestHandle1 = 0x0001;
31 constexpr ChannelId kLocalId = 0x0040;
32 constexpr ChannelId kRemoteId = 0x9042;
33 
BuildConfiguration(hci_android::A2dpCodecType codec=hci_android::A2dpCodecType::kSbc)34 A2dpOffloadManager::Configuration BuildConfiguration(
35     hci_android::A2dpCodecType codec = hci_android::A2dpCodecType::kSbc) {
36   hci_android::A2dpScmsTEnable scms_t_enable;
37   scms_t_enable.enabled = pw::bluetooth::emboss::GenericEnableParam::DISABLE;
38   scms_t_enable.header = 0x00;
39 
40   hci_android::A2dpOffloadCodecInformation codec_information;
41   switch (codec) {
42     case hci_android::A2dpCodecType::kSbc:
43       codec_information.sbc.blocklen_subbands_alloc_method = 0x00;
44       codec_information.sbc.min_bitpool_value = 0x00;
45       codec_information.sbc.max_bitpool_value = 0xFF;
46       memset(codec_information.sbc.reserved,
47              0,
48              sizeof(codec_information.sbc.reserved));
49       break;
50     case hci_android::A2dpCodecType::kAac:
51       codec_information.aac.object_type = 0x00;
52       codec_information.aac.variable_bit_rate =
53           hci_android::A2dpAacEnableVariableBitRate::kDisable;
54       memset(codec_information.aac.reserved,
55              0,
56              sizeof(codec_information.aac.reserved));
57       break;
58     case hci_android::A2dpCodecType::kLdac:
59       codec_information.ldac.vendor_id = 0x0000012D;
60       codec_information.ldac.codec_id = 0x00AA;
61       codec_information.ldac.bitrate_index =
62           hci_android::A2dpBitrateIndex::kLow;
63       codec_information.ldac.ldac_channel_mode =
64           hci_android::A2dpLdacChannelMode::kStereo;
65       memset(codec_information.ldac.reserved,
66              0,
67              sizeof(codec_information.ldac.reserved));
68       break;
69     default:
70       memset(codec_information.aptx.reserved,
71              0,
72              sizeof(codec_information.aptx.reserved));
73       break;
74   }
75 
76   A2dpOffloadManager::Configuration config;
77   config.codec = codec;
78   config.max_latency = 0xFFFF;
79   config.scms_t_enable = scms_t_enable;
80   config.sampling_frequency = hci_android::A2dpSamplingFrequency::k44100Hz;
81   config.bits_per_sample = hci_android::A2dpBitsPerSample::k16BitsPerSample;
82   config.channel_mode = hci_android::A2dpChannelMode::kMono;
83   config.encoded_audio_bit_rate = 0x0;
84   config.codec_information = codec_information;
85 
86   return config;
87 }
88 
89 using TestingBase = FakeDispatcherControllerTest<MockController>;
90 
91 class A2dpOffloadTest : public TestingBase {
92  public:
93   A2dpOffloadTest() = default;
94   ~A2dpOffloadTest() override = default;
95 
SetUp()96   void SetUp() override {
97     TestingBase::SetUp();
98 
99     offload_mgr_ =
100         std::make_unique<A2dpOffloadManager>(cmd_channel()->AsWeakPtr());
101   }
102 
TearDown()103   void TearDown() override { TestingBase::TearDown(); }
104 
offload_mgr() const105   A2dpOffloadManager* offload_mgr() const { return offload_mgr_.get(); }
106 
107  private:
108   std::unique_ptr<A2dpOffloadManager> offload_mgr_;
109 
110   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(A2dpOffloadTest);
111 };
112 
113 class StartA2dpOffloadTest
114     : public A2dpOffloadTest,
115       public ::testing::WithParamInterface<hci_android::A2dpCodecType> {};
116 
TEST_P(StartA2dpOffloadTest,StartA2dpOffloadSuccess)117 TEST_P(StartA2dpOffloadTest, StartA2dpOffloadSuccess) {
118   const hci_android::A2dpCodecType codec = GetParam();
119   A2dpOffloadManager::Configuration config = BuildConfiguration(codec);
120 
121   const auto command_complete =
122       CommandCompletePacket(hci_android::kA2dpOffloadCommand,
123                             pw::bluetooth::emboss::StatusCode::SUCCESS);
124   EXPECT_CMD_PACKET_OUT(
125       test_device(),
126       StartA2dpOffloadRequest(config, kTestHandle1, kRemoteId, kMaxMTU),
127       &command_complete);
128 
129   std::optional<hci::Result<>> start_result;
130   offload_mgr()->StartA2dpOffload(
131       config,
132       kLocalId,
133       kRemoteId,
134       kTestHandle1,
135       kMaxMTU,
136       [&start_result](auto res) {
137         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
138         start_result = res;
139       });
140   RunUntilIdle();
141   EXPECT_TRUE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
142   EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
143   ASSERT_TRUE(start_result.has_value());
144   EXPECT_TRUE(start_result->is_ok());
145 }
146 
147 const std::vector<hci_android::A2dpCodecType> kA2dpCodecTypeParams = {
148     hci_android::A2dpCodecType::kSbc,
149     hci_android::A2dpCodecType::kAac,
150     hci_android::A2dpCodecType::kLdac,
151     hci_android::A2dpCodecType::kAptx};
152 INSTANTIATE_TEST_SUITE_P(ChannelManagerTest,
153                          StartA2dpOffloadTest,
154                          ::testing::ValuesIn(kA2dpCodecTypeParams));
155 
TEST_F(A2dpOffloadTest,StartA2dpOffloadInvalidConfiguration)156 TEST_F(A2dpOffloadTest, StartA2dpOffloadInvalidConfiguration) {
157   A2dpOffloadManager::Configuration config = BuildConfiguration();
158 
159   const auto command_complete = CommandCompletePacket(
160       hci_android::kA2dpOffloadCommand,
161       pw::bluetooth::emboss::StatusCode::INVALID_HCI_COMMAND_PARAMETERS);
162   EXPECT_CMD_PACKET_OUT(
163       test_device(),
164       StartA2dpOffloadRequest(config, kTestHandle1, kRemoteId, kMaxMTU),
165       &command_complete);
166 
167   std::optional<hci::Result<>> start_result;
168   offload_mgr()->StartA2dpOffload(
169       config,
170       kLocalId,
171       kRemoteId,
172       kTestHandle1,
173       kMaxMTU,
174       [&start_result](auto res) {
175         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::
176                                INVALID_HCI_COMMAND_PARAMETERS),
177                   res);
178         start_result = res;
179       });
180   RunUntilIdle();
181   EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
182   ASSERT_TRUE(start_result.has_value());
183   EXPECT_TRUE(start_result->is_error());
184 }
185 
TEST_F(A2dpOffloadTest,StartAndStopA2dpOffloadSuccess)186 TEST_F(A2dpOffloadTest, StartAndStopA2dpOffloadSuccess) {
187   A2dpOffloadManager::Configuration config = BuildConfiguration();
188 
189   const auto command_complete =
190       CommandCompletePacket(hci_android::kA2dpOffloadCommand,
191                             pw::bluetooth::emboss::StatusCode::SUCCESS);
192   EXPECT_CMD_PACKET_OUT(
193       test_device(),
194       StartA2dpOffloadRequest(config, kTestHandle1, kRemoteId, kMaxMTU),
195       &command_complete);
196 
197   std::optional<hci::Result<>> start_result;
198   offload_mgr()->StartA2dpOffload(
199       config,
200       kLocalId,
201       kRemoteId,
202       kTestHandle1,
203       kMaxMTU,
204       [&start_result](auto res) {
205         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
206         start_result = res;
207       });
208   RunUntilIdle();
209   EXPECT_TRUE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
210   EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
211   ASSERT_TRUE(start_result.has_value());
212   EXPECT_TRUE(start_result->is_ok());
213 
214   EXPECT_CMD_PACKET_OUT(
215       test_device(), StopA2dpOffloadRequest(), &command_complete);
216 
217   std::optional<hci::Result<>> stop_result;
218   offload_mgr()->RequestStopA2dpOffload(
219       kLocalId, kTestHandle1, [&stop_result](auto res) {
220         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
221         stop_result = res;
222       });
223   RunUntilIdle();
224   EXPECT_FALSE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
225   EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
226   ASSERT_TRUE(stop_result.has_value());
227   EXPECT_TRUE(stop_result->is_ok());
228 }
229 
TEST_F(A2dpOffloadTest,StartA2dpOffloadAlreadyStarted)230 TEST_F(A2dpOffloadTest, StartA2dpOffloadAlreadyStarted) {
231   A2dpOffloadManager::Configuration config = BuildConfiguration();
232 
233   const auto command_complete =
234       CommandCompletePacket(hci_android::kA2dpOffloadCommand,
235                             pw::bluetooth::emboss::StatusCode::SUCCESS);
236   EXPECT_CMD_PACKET_OUT(
237       test_device(),
238       StartA2dpOffloadRequest(config, kTestHandle1, kRemoteId, kMaxMTU),
239       &command_complete);
240 
241   std::optional<hci::Result<>> start_result;
242   offload_mgr()->StartA2dpOffload(
243       config,
244       kLocalId,
245       kRemoteId,
246       kTestHandle1,
247       kMaxMTU,
248       [&start_result](auto res) {
249         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
250         start_result = res;
251       });
252   RunUntilIdle();
253   EXPECT_TRUE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
254   EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
255   ASSERT_TRUE(start_result.has_value());
256   EXPECT_TRUE(start_result->is_ok());
257 
258   start_result.reset();
259   offload_mgr()->StartA2dpOffload(config,
260                                   kLocalId,
261                                   kRemoteId,
262                                   kTestHandle1,
263                                   kMaxMTU,
264                                   [&start_result](auto res) {
265                                     EXPECT_EQ(ToResult(HostError::kInProgress),
266                                               res);
267                                     start_result = res;
268                                   });
269   RunUntilIdle();
270   EXPECT_TRUE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
271   ASSERT_TRUE(start_result.has_value());
272   EXPECT_TRUE(start_result->is_error());
273 }
274 
TEST_F(A2dpOffloadTest,StartA2dpOffloadStillStarting)275 TEST_F(A2dpOffloadTest, StartA2dpOffloadStillStarting) {
276   A2dpOffloadManager::Configuration config = BuildConfiguration();
277 
278   const auto command_complete =
279       CommandCompletePacket(hci_android::kA2dpOffloadCommand,
280                             pw::bluetooth::emboss::StatusCode::SUCCESS);
281   EXPECT_CMD_PACKET_OUT(
282       test_device(),
283       StartA2dpOffloadRequest(config, kTestHandle1, kRemoteId, kMaxMTU),
284       &command_complete);
285 
286   std::optional<hci::Result<>> start_result;
287   offload_mgr()->StartA2dpOffload(
288       config,
289       kLocalId,
290       kRemoteId,
291       kTestHandle1,
292       kMaxMTU,
293       [&start_result](auto res) {
294         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
295         start_result = res;
296       });
297   EXPECT_FALSE(start_result.has_value());
298 
299   offload_mgr()->StartA2dpOffload(config,
300                                   kLocalId,
301                                   kRemoteId,
302                                   kTestHandle1,
303                                   kMaxMTU,
304                                   [&start_result](auto res) {
305                                     EXPECT_EQ(ToResult(HostError::kInProgress),
306                                               res);
307                                     start_result = res;
308                                   });
309   RunUntilIdle();
310   EXPECT_TRUE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
311   EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
312   ASSERT_TRUE(start_result.has_value());
313   EXPECT_TRUE(start_result->is_ok());
314 }
315 
TEST_F(A2dpOffloadTest,StartA2dpOffloadStillStopping)316 TEST_F(A2dpOffloadTest, StartA2dpOffloadStillStopping) {
317   A2dpOffloadManager::Configuration config = BuildConfiguration();
318 
319   const auto command_complete =
320       CommandCompletePacket(hci_android::kA2dpOffloadCommand,
321                             pw::bluetooth::emboss::StatusCode::SUCCESS);
322   EXPECT_CMD_PACKET_OUT(
323       test_device(),
324       StartA2dpOffloadRequest(config, kTestHandle1, kRemoteId, kMaxMTU),
325       &command_complete);
326 
327   std::optional<hci::Result<>> start_result;
328   offload_mgr()->StartA2dpOffload(
329       config,
330       kLocalId,
331       kRemoteId,
332       kTestHandle1,
333       kMaxMTU,
334       [&start_result](auto res) {
335         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
336         start_result = res;
337       });
338   RunUntilIdle();
339   EXPECT_TRUE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
340   EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
341   ASSERT_TRUE(start_result.has_value());
342   EXPECT_TRUE(start_result->is_ok());
343 
344   EXPECT_CMD_PACKET_OUT(
345       test_device(), StopA2dpOffloadRequest(), &command_complete);
346 
347   std::optional<hci::Result<>> stop_result;
348   offload_mgr()->RequestStopA2dpOffload(
349       kLocalId, kTestHandle1, [&stop_result](auto res) {
350         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
351         stop_result = res;
352       });
353   EXPECT_FALSE(stop_result.has_value());
354 
355   start_result.reset();
356   offload_mgr()->StartA2dpOffload(config,
357                                   kLocalId,
358                                   kRemoteId,
359                                   kTestHandle1,
360                                   kMaxMTU,
361                                   [&start_result](auto res) {
362                                     EXPECT_EQ(ToResult(HostError::kInProgress),
363                                               res);
364                                     start_result = res;
365                                   });
366   RunUntilIdle();
367   EXPECT_FALSE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
368   EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
369   ASSERT_TRUE(start_result.has_value());
370   EXPECT_TRUE(start_result->is_error());
371   ASSERT_TRUE(stop_result.has_value());
372   EXPECT_TRUE(stop_result->is_ok());
373 }
374 
TEST_F(A2dpOffloadTest,StopA2dpOffloadStillStarting)375 TEST_F(A2dpOffloadTest, StopA2dpOffloadStillStarting) {
376   A2dpOffloadManager::Configuration config = BuildConfiguration();
377 
378   const auto command_complete =
379       CommandCompletePacket(hci_android::kA2dpOffloadCommand,
380                             pw::bluetooth::emboss::StatusCode::SUCCESS);
381   EXPECT_CMD_PACKET_OUT(
382       test_device(),
383       StartA2dpOffloadRequest(config, kTestHandle1, kRemoteId, kMaxMTU),
384       &command_complete);
385 
386   std::optional<hci::Result<>> start_result;
387   offload_mgr()->StartA2dpOffload(
388       config,
389       kLocalId,
390       kRemoteId,
391       kTestHandle1,
392       kMaxMTU,
393       [&start_result](auto res) {
394         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
395         start_result = res;
396       });
397   EXPECT_FALSE(start_result.has_value());
398 
399   EXPECT_CMD_PACKET_OUT(
400       test_device(), StopA2dpOffloadRequest(), &command_complete);
401 
402   std::optional<hci::Result<>> stop_result;
403   offload_mgr()->RequestStopA2dpOffload(
404       kLocalId, kTestHandle1, [&stop_result](auto res) {
405         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
406         stop_result = res;
407       });
408   RunUntilIdle();
409   EXPECT_FALSE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
410   EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
411   ASSERT_TRUE(start_result.has_value());
412   EXPECT_TRUE(start_result->is_ok());
413   ASSERT_TRUE(stop_result.has_value());
414   EXPECT_TRUE(stop_result->is_ok());
415 }
416 
TEST_F(A2dpOffloadTest,StopA2dpOffloadStillStopping)417 TEST_F(A2dpOffloadTest, StopA2dpOffloadStillStopping) {
418   A2dpOffloadManager::Configuration config = BuildConfiguration();
419 
420   const auto command_complete =
421       CommandCompletePacket(hci_android::kA2dpOffloadCommand,
422                             pw::bluetooth::emboss::StatusCode::SUCCESS);
423   EXPECT_CMD_PACKET_OUT(
424       test_device(),
425       StartA2dpOffloadRequest(config, kTestHandle1, kRemoteId, kMaxMTU),
426       &command_complete);
427 
428   std::optional<hci::Result<>> start_result;
429   offload_mgr()->StartA2dpOffload(
430       config,
431       kLocalId,
432       kRemoteId,
433       kTestHandle1,
434       kMaxMTU,
435       [&start_result](auto res) {
436         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
437         start_result = res;
438       });
439   RunUntilIdle();
440   EXPECT_TRUE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
441   EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
442   ASSERT_TRUE(start_result.has_value());
443   EXPECT_TRUE(start_result->is_ok());
444 
445   EXPECT_CMD_PACKET_OUT(
446       test_device(), StopA2dpOffloadRequest(), &command_complete);
447 
448   std::optional<hci::Result<>> stop_result;
449   offload_mgr()->RequestStopA2dpOffload(
450       kLocalId, kTestHandle1, [&stop_result](auto res) {
451         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
452         stop_result = res;
453       });
454   EXPECT_FALSE(stop_result.has_value());
455 
456   offload_mgr()->RequestStopA2dpOffload(
457       kLocalId, kTestHandle1, [&stop_result](auto res) {
458         EXPECT_EQ(ToResult(HostError::kInProgress), res);
459         stop_result = res;
460       });
461   RunUntilIdle();
462   EXPECT_FALSE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
463   EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
464   ASSERT_TRUE(stop_result.has_value());
465   EXPECT_TRUE(stop_result->is_ok());
466 }
467 
TEST_F(A2dpOffloadTest,StopA2dpOffloadAlreadyStopped)468 TEST_F(A2dpOffloadTest, StopA2dpOffloadAlreadyStopped) {
469   std::optional<hci::Result<>> stop_result;
470   offload_mgr()->RequestStopA2dpOffload(
471       kLocalId, kTestHandle1, [&stop_result](auto res) {
472         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
473         stop_result = res;
474       });
475   RunUntilIdle();
476   EXPECT_FALSE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
477   ASSERT_TRUE(stop_result.has_value());
478   EXPECT_TRUE(stop_result->is_ok());
479 }
480 
TEST_F(A2dpOffloadTest,A2dpOffloadOnlyOneChannel)481 TEST_F(A2dpOffloadTest, A2dpOffloadOnlyOneChannel) {
482   A2dpOffloadManager::Configuration config = BuildConfiguration();
483 
484   const auto command_complete =
485       CommandCompletePacket(hci_android::kA2dpOffloadCommand,
486                             pw::bluetooth::emboss::StatusCode::SUCCESS);
487   EXPECT_CMD_PACKET_OUT(
488       test_device(),
489       StartA2dpOffloadRequest(config, kTestHandle1, kRemoteId, kMaxMTU),
490       &command_complete);
491 
492   std::optional<hci::Result<>> start_result_0;
493   offload_mgr()->StartA2dpOffload(
494       config,
495       kLocalId,
496       kRemoteId,
497       kTestHandle1,
498       kMaxMTU,
499       [&start_result_0](auto res) {
500         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
501         start_result_0 = res;
502       });
503   RunUntilIdle();
504   EXPECT_TRUE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
505   EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
506   ASSERT_TRUE(start_result_0.has_value());
507   EXPECT_TRUE(start_result_0->is_ok());
508 
509   std::optional<hci::Result<>> start_result_1;
510   offload_mgr()->StartA2dpOffload(config,
511                                   kLocalId + 1,
512                                   kRemoteId + 1,
513                                   kTestHandle1,
514                                   kMaxMTU,
515                                   [&start_result_1](auto res) {
516                                     EXPECT_EQ(ToResult(HostError::kInProgress),
517                                               res);
518                                     start_result_1 = res;
519                                   });
520   RunUntilIdle();
521   EXPECT_TRUE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
522   EXPECT_FALSE(offload_mgr()->IsChannelOffloaded(kLocalId + 1, kTestHandle1));
523   ASSERT_TRUE(start_result_1.has_value());
524   EXPECT_TRUE(start_result_1->is_error());
525 }
526 
TEST_F(A2dpOffloadTest,DifferentChannelCannotStopA2dpOffloading)527 TEST_F(A2dpOffloadTest, DifferentChannelCannotStopA2dpOffloading) {
528   A2dpOffloadManager::Configuration config = BuildConfiguration();
529 
530   const auto command_complete =
531       CommandCompletePacket(hci_android::kA2dpOffloadCommand,
532                             pw::bluetooth::emboss::StatusCode::SUCCESS);
533   EXPECT_CMD_PACKET_OUT(
534       test_device(),
535       StartA2dpOffloadRequest(config, kTestHandle1, kRemoteId, kMaxMTU),
536       &command_complete);
537 
538   std::optional<hci::Result<>> start_result;
539   offload_mgr()->StartA2dpOffload(
540       config,
541       kLocalId,
542       kRemoteId,
543       kTestHandle1,
544       kMaxMTU,
545       [&start_result](auto res) {
546         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
547         start_result = res;
548       });
549   RunUntilIdle();
550   EXPECT_TRUE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
551   EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
552   ASSERT_TRUE(start_result.has_value());
553   EXPECT_TRUE(start_result->is_ok());
554 
555   std::optional<hci::Result<>> stop_result;
556   offload_mgr()->RequestStopA2dpOffload(
557       kLocalId + 1, kTestHandle1 + 1, [&stop_result](auto res) {
558         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
559         stop_result = res;
560       });
561   RunUntilIdle();
562   EXPECT_TRUE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
563   ASSERT_TRUE(stop_result.has_value());
564   EXPECT_TRUE(stop_result->is_ok());
565 
566   EXPECT_CMD_PACKET_OUT(
567       test_device(), StopA2dpOffloadRequest(), &command_complete);
568 
569   // Can still stop it from the correct one.
570   stop_result = std::nullopt;
571   offload_mgr()->RequestStopA2dpOffload(
572       kLocalId, kTestHandle1, [&stop_result](auto res) {
573         EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
574         stop_result = res;
575       });
576   RunUntilIdle();
577   EXPECT_FALSE(offload_mgr()->IsChannelOffloaded(kLocalId, kTestHandle1));
578   ASSERT_TRUE(stop_result.has_value());
579   EXPECT_TRUE(stop_result->is_ok());
580 }
581 
582 }  // namespace
583 }  // namespace bt::l2cap
584