xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/l2cap/logical_link_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/l2cap/logical_link.h"
16 
17 #include <memory>
18 #include <optional>
19 
20 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
21 #include "pw_bluetooth_sapphire/internal/host/hci/connection.h"
22 #include "pw_bluetooth_sapphire/internal/host/l2cap/channel.h"
23 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
24 #include "pw_bluetooth_sapphire/internal/host/l2cap/test_packets.h"
25 #include "pw_bluetooth_sapphire/internal/host/testing/controller_test.h"
26 #include "pw_bluetooth_sapphire/internal/host/testing/mock_controller.h"
27 #include "pw_bluetooth_sapphire/internal/host/testing/test_packets.h"
28 #include "pw_bluetooth_sapphire/internal/host/transport/acl_data_channel.h"
29 #include "pw_bluetooth_sapphire/internal/host/transport/link_type.h"
30 
31 namespace bt::l2cap::internal {
32 namespace {
33 using Conn = hci::Connection;
34 
35 using TestingBase =
36     bt::testing::FakeDispatcherControllerTest<bt::testing::MockController>;
37 
38 const hci_spec::ConnectionHandle kConnHandle = 0x0001;
39 
40 class LogicalLinkTest : public TestingBase {
41  public:
42   LogicalLinkTest() = default;
43   ~LogicalLinkTest() override = default;
44   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LogicalLinkTest);
45 
46  protected:
SetUp()47   void SetUp() override {
48     TestingBase::SetUp();
49     InitializeACLDataChannel();
50 
51     NewLogicalLink();
52   }
TearDown()53   void TearDown() override {
54     if (link_) {
55       link_->Close();
56       link_ = nullptr;
57     }
58 
59     a2dp_offload_manager_ = nullptr;
60 
61     TestingBase::TearDown();
62   }
NewLogicalLink(bt::LinkType type=bt::LinkType::kLE,bool random_channel_ids=true)63   void NewLogicalLink(bt::LinkType type = bt::LinkType::kLE,
64                       bool random_channel_ids = true) {
65     const size_t kMaxPayload = kDefaultMTU;
66     auto query_service_cb = [](hci_spec::ConnectionHandle, Psm) {
67       return std::nullopt;
68     };
69     a2dp_offload_manager_ = std::make_unique<A2dpOffloadManager>(
70         transport()->command_channel()->AsWeakPtr());
71     link_ = std::make_unique<LogicalLink>(
72         kConnHandle,
73         type,
74         pw::bluetooth::emboss::ConnectionRole::CENTRAL,
75         kMaxPayload,
76         std::move(query_service_cb),
77         transport()->acl_data_channel(),
78         transport()->command_channel(),
79         random_channel_ids,
80         *a2dp_offload_manager_,
81         dispatcher());
82   }
ResetAndCreateNewLogicalLink(LinkType type=LinkType::kACL,bool random_channel_ids=true)83   void ResetAndCreateNewLogicalLink(LinkType type = LinkType::kACL,
84                                     bool random_channel_ids = true) {
85     link()->Close();
86     DeleteLink();
87     NewLogicalLink(type, random_channel_ids);
88   }
89 
link() const90   LogicalLink* link() const { return link_.get(); }
DeleteLink()91   void DeleteLink() { link_ = nullptr; }
92 
93  private:
94   std::unique_ptr<LogicalLink> link_;
95   std::unique_ptr<A2dpOffloadManager> a2dp_offload_manager_;
96 };
97 
98 struct QueueAclConnectionRetVal {
99   l2cap::CommandId extended_features_id;
100   l2cap::CommandId fixed_channels_supported_id;
101 };
102 
103 static constexpr l2cap::ExtendedFeatures kExtendedFeatures =
104     l2cap::kExtendedFeaturesBitEnhancedRetransmission;
105 
106 using LogicalLinkDeathTest = LogicalLinkTest;
107 
TEST_F(LogicalLinkDeathTest,DestructedWithoutClosingDies)108 TEST_F(LogicalLinkDeathTest, DestructedWithoutClosingDies) {
109   // Deleting the link without calling `Close` on it should trigger an
110   // assertion.
111   ASSERT_DEATH_IF_SUPPORTED(DeleteLink(), ".*closed.*");
112 }
113 
TEST_F(LogicalLinkTest,FixedChannelHasCorrectMtu)114 TEST_F(LogicalLinkTest, FixedChannelHasCorrectMtu) {
115   Channel::WeakPtr fixed_chan = link()->OpenFixedChannel(kATTChannelId);
116   ASSERT_TRUE(fixed_chan.is_alive());
117   EXPECT_EQ(kMaxMTU, fixed_chan->max_rx_sdu_size());
118   EXPECT_EQ(kMaxMTU, fixed_chan->max_tx_sdu_size());
119 }
120 
TEST_F(LogicalLinkTest,DropsBroadcastPackets)121 TEST_F(LogicalLinkTest, DropsBroadcastPackets) {
122   ResetAndCreateNewLogicalLink();
123 
124   QueueAclConnectionRetVal cmd_ids;
125   cmd_ids.extended_features_id = 1;
126   cmd_ids.fixed_channels_supported_id = 2;
127 
128   const auto kExtFeaturesRsp = l2cap::testing::AclExtFeaturesInfoRsp(
129       cmd_ids.extended_features_id, kConnHandle, kExtendedFeatures);
130   EXPECT_ACL_PACKET_OUT(test_device(),
131                         l2cap::testing::AclExtFeaturesInfoReq(
132                             cmd_ids.extended_features_id, kConnHandle),
133                         &kExtFeaturesRsp);
134   EXPECT_ACL_PACKET_OUT(test_device(),
135                         l2cap::testing::AclFixedChannelsSupportedInfoReq(
136                             cmd_ids.fixed_channels_supported_id, kConnHandle));
137 
138   Channel::WeakPtr connectionless_chan =
139       link()->OpenFixedChannel(kConnectionlessChannelId);
140   ASSERT_TRUE(connectionless_chan.is_alive());
141 
142   size_t rx_count = 0;
143   bool activated = connectionless_chan->Activate(
144       [&](ByteBufferPtr) { rx_count++; }, []() {});
145   ASSERT_TRUE(activated);
146 
147   StaticByteBuffer group_frame(0x0A,
148                                0x00,  // Length (PSM + info = 10)
149                                0x02,
150                                0x00,  // Connectionless data channel
151                                0xF0,
152                                0x0F,  // PSM
153                                'S',
154                                'a',
155                                'p',
156                                'p',
157                                'h',
158                                'i',
159                                'r',
160                                'e'  // Info Payload
161   );
162   hci::ACLDataPacketPtr packet = hci::ACLDataPacket::New(
163       kConnHandle,
164       hci_spec::ACLPacketBoundaryFlag::kCompletePDU,
165       hci_spec::ACLBroadcastFlag::kActivePeripheralBroadcast,
166       static_cast<uint16_t>(group_frame.size()));
167   ASSERT_TRUE(packet);
168   packet->mutable_view()->mutable_payload_data().Write(group_frame);
169 
170   link()->HandleRxPacket(std::move(packet));
171 
172   // Should be dropped.
173   EXPECT_EQ(0u, rx_count);
174 }
175 
176 // LE links are unsupported, so result should be an error. No command should be
177 // sent.
TEST_F(LogicalLinkTest,SetBrEdrAutomaticFlushTimeoutFailsForLELink)178 TEST_F(LogicalLinkTest, SetBrEdrAutomaticFlushTimeoutFailsForLELink) {
179   constexpr std::chrono::milliseconds kTimeout(100);
180   ResetAndCreateNewLogicalLink(LinkType::kLE);
181 
182   bool cb_called = false;
183   link()->SetBrEdrAutomaticFlushTimeout(kTimeout, [&](auto result) {
184     cb_called = true;
185     ASSERT_TRUE(result.is_error());
186     EXPECT_EQ(
187         ToResult(
188             pw::bluetooth::emboss::StatusCode::INVALID_HCI_COMMAND_PARAMETERS),
189         result.error_value());
190   });
191   EXPECT_TRUE(cb_called);
192 }
193 
TEST_F(LogicalLinkTest,SetAutomaticFlushTimeoutSuccess)194 TEST_F(LogicalLinkTest, SetAutomaticFlushTimeoutSuccess) {
195   ResetAndCreateNewLogicalLink();
196 
197   QueueAclConnectionRetVal cmd_ids;
198   cmd_ids.extended_features_id = 1;
199   cmd_ids.fixed_channels_supported_id = 2;
200 
201   const auto kExtFeaturesRsp = l2cap::testing::AclExtFeaturesInfoRsp(
202       cmd_ids.extended_features_id, kConnHandle, kExtendedFeatures);
203   EXPECT_ACL_PACKET_OUT(test_device(),
204                         l2cap::testing::AclExtFeaturesInfoReq(
205                             cmd_ids.extended_features_id, kConnHandle),
206                         &kExtFeaturesRsp);
207   EXPECT_ACL_PACKET_OUT(test_device(),
208                         l2cap::testing::AclFixedChannelsSupportedInfoReq(
209                             cmd_ids.fixed_channels_supported_id, kConnHandle));
210 
211   std::optional<hci::Result<>> cb_status;
212   auto result_cb = [&](auto status) { cb_status = status; };
213 
214   // Test command complete error
215   const auto kCommandCompleteError = bt::testing::CommandCompletePacket(
216       hci_spec::kWriteAutomaticFlushTimeout,
217       pw::bluetooth::emboss::StatusCode::UNKNOWN_CONNECTION_ID);
218   EXPECT_CMD_PACKET_OUT(
219       test_device(),
220       bt::testing::WriteAutomaticFlushTimeoutPacket(link()->handle(), 0),
221       &kCommandCompleteError);
222   link()->SetBrEdrAutomaticFlushTimeout(
223       pw::chrono::SystemClock::duration::max(), result_cb);
224   RunUntilIdle();
225   ASSERT_TRUE(cb_status.has_value());
226   ASSERT_TRUE(cb_status->is_error());
227   EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::UNKNOWN_CONNECTION_ID),
228             *cb_status);
229   cb_status.reset();
230 
231   // Test flush timeout = 0 (no command should be sent)
232   link()->SetBrEdrAutomaticFlushTimeout(std::chrono::milliseconds(0),
233                                         result_cb);
234   RunUntilIdle();
235   ASSERT_TRUE(cb_status.has_value());
236   EXPECT_TRUE(cb_status->is_error());
237   EXPECT_EQ(
238       ToResult(
239           pw::bluetooth::emboss::StatusCode::INVALID_HCI_COMMAND_PARAMETERS),
240       *cb_status);
241 
242   // Test infinite flush timeout (flush timeout of 0 should be sent).
243   const auto kCommandComplete = bt::testing::CommandCompletePacket(
244       hci_spec::kWriteAutomaticFlushTimeout,
245       pw::bluetooth::emboss::StatusCode::SUCCESS);
246   EXPECT_CMD_PACKET_OUT(
247       test_device(),
248       bt::testing::WriteAutomaticFlushTimeoutPacket(link()->handle(), 0),
249       &kCommandComplete);
250   link()->SetBrEdrAutomaticFlushTimeout(
251       pw::chrono::SystemClock::duration::max(), result_cb);
252   RunUntilIdle();
253   ASSERT_TRUE(cb_status.has_value());
254   EXPECT_EQ(fit::ok(), *cb_status);
255   cb_status.reset();
256 
257   // Test msec to parameter conversion
258   // (hci_spec::kMaxAutomaticFlushTimeoutDuration(1279) * conversion_factor(1.6)
259   // = 2046).
260   EXPECT_CMD_PACKET_OUT(
261       test_device(),
262       bt::testing::WriteAutomaticFlushTimeoutPacket(link()->handle(), 2046),
263       &kCommandComplete);
264   link()->SetBrEdrAutomaticFlushTimeout(
265       hci_spec::kMaxAutomaticFlushTimeoutDuration, result_cb);
266   RunUntilIdle();
267   ASSERT_TRUE(cb_status.has_value());
268   EXPECT_EQ(fit::ok(), *cb_status);
269   cb_status.reset();
270 
271   // Test too large flush timeout (no command should be sent).
272   link()->SetBrEdrAutomaticFlushTimeout(
273       hci_spec::kMaxAutomaticFlushTimeoutDuration +
274           std::chrono::milliseconds(1),
275       result_cb);
276   RunUntilIdle();
277   ASSERT_TRUE(cb_status.has_value());
278   EXPECT_TRUE(cb_status->is_error());
279   EXPECT_EQ(
280       ToResult(
281           pw::bluetooth::emboss::StatusCode::INVALID_HCI_COMMAND_PARAMETERS),
282       *cb_status);
283 }
284 
TEST_F(LogicalLinkTest,OpensLeDynamicChannel)285 TEST_F(LogicalLinkTest, OpensLeDynamicChannel) {
286   ResetAndCreateNewLogicalLink(LinkType::kLE, false);
287   static constexpr uint16_t kPsm = 0x015;
288   static constexpr ChannelParameters kParams{
289       .mode = CreditBasedFlowControlMode::kLeCreditBasedFlowControl,
290       .max_rx_sdu_size = std::nullopt,
291       .flush_timeout = std::nullopt,
292   };
293 
294   transport()->acl_data_channel()->SetDataRxHandler(
295       fit::bind_member<&LogicalLink::HandleRxPacket>(link()));
296 
297   const auto req =
298       l2cap::testing::AclLeCreditBasedConnectionReq(1,
299                                                     kConnHandle,
300                                                     kPsm,
301                                                     kFirstDynamicChannelId,
302                                                     kDefaultMTU,
303                                                     kMaxInboundPduPayloadSize,
304                                                     0);
305   const auto rsp = l2cap::testing::AclLeCreditBasedConnectionRsp(
306       /*id=*/1,
307       /*link_handle=*/kConnHandle,
308       /*cid=*/kFirstDynamicChannelId,
309       /*mtu=*/64,
310       /*mps=*/32,
311       /*credits=*/10,
312       /*result=*/LECreditBasedConnectionResult::kSuccess);
313   EXPECT_ACL_PACKET_OUT(test_device(), req, &rsp);
314 
315   WeakPtr<Channel> channel;
316   link()->OpenChannel(
317       kPsm, kParams, [&](auto result) { channel = std::move(result); });
318   RunUntilIdle();
319 
320   ASSERT_TRUE(channel.is_alive());
321 }
322 
323 }  // namespace
324 }  // namespace bt::l2cap::internal
325