• 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/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