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