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