1 // Copyright 2024 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/le_dynamic_channel.h"
16
17 #include <memory>
18
19 #include "pw_async/fake_dispatcher_fixture.h"
20 #include "pw_bluetooth_sapphire/internal/host/l2cap/dynamic_channel_registry.h"
21 #include "pw_bluetooth_sapphire/internal/host/l2cap/fake_signaling_channel.h"
22 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
23 #include "pw_bluetooth_sapphire/internal/host/l2cap/signaling_channel.h"
24 #include "pw_bluetooth_sapphire/internal/host/l2cap/types.h"
25 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
26 #include "pw_unit_test/framework.h"
27
28 namespace bt::l2cap::internal {
29 namespace {
DynamicCid(int16_t channel_number=0)30 constexpr ChannelId DynamicCid(int16_t channel_number = 0) {
31 return kFirstDynamicChannelId + channel_number;
32 }
33
LeConnReq(const LECreditBasedConnectionRequestPayload & payload)34 auto LeConnReq(const LECreditBasedConnectionRequestPayload& payload) {
35 return StaticByteBuffer(LowerBits(payload.le_psm),
36 UpperBits(payload.le_psm),
37 LowerBits(payload.src_cid),
38 UpperBits(payload.src_cid),
39 LowerBits(payload.mtu),
40 UpperBits(payload.mtu),
41 LowerBits(payload.mps),
42 UpperBits(payload.mps),
43 LowerBits(payload.initial_credits),
44 UpperBits(payload.initial_credits));
45 }
46
LeConnRsp(const LECreditBasedConnectionResponsePayload & payload)47 auto LeConnRsp(const LECreditBasedConnectionResponsePayload& payload) {
48 return StaticByteBuffer(LowerBits(payload.dst_cid),
49 UpperBits(payload.dst_cid),
50 LowerBits(payload.mtu),
51 UpperBits(payload.mtu),
52 LowerBits(payload.mps),
53 UpperBits(payload.mps),
54 LowerBits(payload.initial_credits),
55 UpperBits(payload.initial_credits),
56 LowerBits(static_cast<uint16_t>(payload.result)),
57 UpperBits(static_cast<uint16_t>(payload.result)));
58 }
59
DisconnReq(const DisconnectionRequestPayload & payload)60 auto DisconnReq(const DisconnectionRequestPayload& payload) {
61 return StaticByteBuffer(LowerBits(payload.dst_cid),
62 UpperBits(payload.dst_cid),
63 LowerBits(payload.src_cid),
64 UpperBits(payload.src_cid));
65 }
66
DisconnRsp(const DisconnectionResponsePayload & payload)67 auto DisconnRsp(const DisconnectionResponsePayload& payload) {
68 return StaticByteBuffer(LowerBits(payload.dst_cid),
69 UpperBits(payload.dst_cid),
70 LowerBits(payload.src_cid),
71 UpperBits(payload.src_cid));
72 }
73
74 class LeDynamicChannelTest : public pw::async::test::FakeDispatcherFixture {
75 public:
76 LeDynamicChannelTest() = default;
77 ~LeDynamicChannelTest() override = default;
78
79 protected:
80 // Import types for brevity.
81 using DynamicChannelCallback = DynamicChannelRegistry::DynamicChannelCallback;
82 using ServiceRequestCallback = DynamicChannelRegistry::ServiceRequestCallback;
83
84 // TestLoopFixture overrides
SetUp()85 void SetUp() override {
86 channel_close_cb_ = nullptr;
87 inbound_open_cb_ = nullptr;
88 signaling_channel_ =
89 std::make_unique<testing::FakeSignalingChannel>(dispatcher());
90
91 registry_ = std::make_unique<LeDynamicChannelRegistry>(
92 sig(),
93 fit::bind_member<&LeDynamicChannelTest::OnChannelClose>(this),
94 fit::bind_member<&LeDynamicChannelTest::OnServiceRequest>(this),
95 /*random_channel_ids=*/false);
96 }
97
TearDown()98 void TearDown() override {
99 RunUntilIdle();
100 services_.clear();
101 registry_ = nullptr;
102 signaling_channel_ = nullptr;
103 inbound_open_cb_ = nullptr;
104 channel_close_cb_ = nullptr;
105 }
106
DoOpenOutbound(const LECreditBasedConnectionRequestPayload & request,const LECreditBasedConnectionResponsePayload & response,ChannelParameters params,SignalingChannel::Status response_status=SignalingChannel::Status::kSuccess)107 const DynamicChannel* DoOpenOutbound(
108 const LECreditBasedConnectionRequestPayload& request,
109 const LECreditBasedConnectionResponsePayload& response,
110 ChannelParameters params,
111 SignalingChannel::Status response_status =
112 SignalingChannel::Status::kSuccess) {
113 auto req = LeConnReq(request);
114 auto rsp = LeConnRsp(response);
115 EXPECT_OUTBOUND_REQ(*sig(),
116 kLECreditBasedConnectionRequest,
117 req.view(),
118 {response_status, rsp.view()});
119
120 auto channel = std::make_shared<std::optional<const DynamicChannel*>>();
121 registry()->OpenOutbound(
122 request.le_psm, params, [weak = std::weak_ptr(channel)](auto chan) {
123 if (auto channel = weak.lock()) {
124 *channel = chan;
125 }
126 });
127
128 RunUntilIdle();
129 if (HasFatalFailure()) {
130 return nullptr;
131 }
132
133 // We should always get a result, whether it is a success or failure.
134 EXPECT_TRUE(*channel);
135 if (!*channel) {
136 return nullptr;
137 }
138
139 return **channel;
140 }
141
DoOpenInbound(const LECreditBasedConnectionRequestPayload & request,const LECreditBasedConnectionResponsePayload & expected_response)142 bool DoOpenInbound(
143 const LECreditBasedConnectionRequestPayload& request,
144 const LECreditBasedConnectionResponsePayload& expected_response) {
145 auto req = LeConnReq(request);
146 auto rsp = LeConnRsp(expected_response);
147
148 sig()->ReceiveExpect(
149 kLECreditBasedConnectionRequest, req.view(), rsp.view());
150
151 RunUntilIdle();
152 if (HasFatalFailure()) {
153 return false;
154 }
155
156 return true;
157 }
158
DoCloseOutbound(const DisconnectionRequestPayload & request,const DisconnectionResponsePayload & response)159 bool DoCloseOutbound(const DisconnectionRequestPayload& request,
160 const DisconnectionResponsePayload& response) {
161 auto req = DisconnReq(request);
162 auto rsp = DisconnRsp(response);
163 EXPECT_OUTBOUND_REQ(*sig(),
164 kDisconnectionRequest,
165 req.view(),
166 {SignalingChannel::Status::kSuccess, rsp.view()});
167 bool channel_close_cb_called = false;
168 registry()->CloseChannel(request.src_cid,
169 [&] { channel_close_cb_called = true; });
170 RunUntilIdle();
171 if (HasFatalFailure()) {
172 return false;
173 }
174
175 return channel_close_cb_called;
176 }
177
DoCloseOutbound(const DisconnectionRequestPayload & request)178 bool DoCloseOutbound(const DisconnectionRequestPayload& request) {
179 DisconnectionResponsePayload response = {request.dst_cid, request.src_cid};
180 return DoCloseOutbound(request, response);
181 }
182
sig() const183 testing::FakeSignalingChannel* sig() const {
184 return signaling_channel_.get();
185 }
186
registry() const187 LeDynamicChannelRegistry* registry() const { return registry_.get(); }
188
set_channel_close_cb(DynamicChannelCallback close_cb)189 void set_channel_close_cb(DynamicChannelCallback close_cb) {
190 channel_close_cb_ = std::move(close_cb);
191 }
192
RegisterService(Psm psm,ChannelParameters params)193 bool RegisterService(Psm psm, ChannelParameters params) {
194 auto [_, result] = services_.insert(
195 {psm,
196 DynamicChannelRegistry::ServiceInfo{
197 params,
198 fit::bind_member(this, &LeDynamicChannelTest::OnInboundChannel)}});
199 return result;
200 }
201
SetNextInboundChannel(std::optional<const DynamicChannel * > * out)202 void SetNextInboundChannel(std::optional<const DynamicChannel*>* out) {
203 ASSERT_EQ(inbound_open_cb_, nullptr);
204 inbound_open_cb_ = [out](const DynamicChannel* channel) { *out = channel; };
205 }
206
207 private:
OnChannelClose(const DynamicChannel * channel)208 void OnChannelClose(const DynamicChannel* channel) {
209 if (channel_close_cb_) {
210 channel_close_cb_(channel);
211 }
212 }
213
OnInboundChannel(const DynamicChannel * channel)214 void OnInboundChannel(const DynamicChannel* channel) {
215 if (inbound_open_cb_) {
216 std::move(inbound_open_cb_)(channel);
217 }
218 }
219
OnServiceRequest(Psm psm)220 std::optional<DynamicChannelRegistry::ServiceInfo> OnServiceRequest(Psm psm) {
221 auto iter = services_.find(psm);
222 if (iter == services_.end()) {
223 return std::nullopt;
224 }
225
226 return DynamicChannelRegistry::ServiceInfo(iter->second.channel_params,
227 iter->second.channel_cb.share());
228 }
229
230 DynamicChannelCallback channel_close_cb_;
231 DynamicChannelCallback inbound_open_cb_;
232 std::unordered_map<Psm, DynamicChannelRegistry::ServiceInfo> services_;
233 std::unique_ptr<testing::FakeSignalingChannel> signaling_channel_;
234 std::unique_ptr<LeDynamicChannelRegistry> registry_;
235
236 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LeDynamicChannelTest);
237 };
238
TEST_F(LeDynamicChannelTest,StateToString)239 TEST_F(LeDynamicChannelTest, StateToString) {
240 constexpr std::array<std::pair<LeDynamicChannel::State, const char*>, 4>
241 kCases{{
242 {{.exchanged_connection_request = false,
243 .exchanged_connection_response = false,
244 .exchanged_disconnect_request = false},
245 "{exchanged_connection_request: false, "
246 "exchanged_connection_response: false, "
247 "exchanged_disconnect_request: false}"},
248 {{.exchanged_connection_request = false,
249 .exchanged_connection_response = false,
250 .exchanged_disconnect_request = true},
251 "{exchanged_connection_request: false, "
252 "exchanged_connection_response: false, "
253 "exchanged_disconnect_request: true}"},
254 {{.exchanged_connection_request = false,
255 .exchanged_connection_response = true,
256 .exchanged_disconnect_request = false},
257 "{exchanged_connection_request: false, "
258 "exchanged_connection_response: true, "
259 "exchanged_disconnect_request: false}"},
260 {{.exchanged_connection_request = true,
261 .exchanged_connection_response = false,
262 .exchanged_disconnect_request = false},
263 "{exchanged_connection_request: true, "
264 "exchanged_connection_response: false, "
265 "exchanged_disconnect_request: false}"},
266 }};
267 for (const auto& [state, expected] : kCases) {
268 EXPECT_EQ(state.ToString(), expected);
269 }
270 }
271
TEST_F(LeDynamicChannelTest,OpenOutboundDefaultParametersCloseOutbound)272 TEST_F(LeDynamicChannelTest, OpenOutboundDefaultParametersCloseOutbound) {
273 static constexpr auto kMode =
274 CreditBasedFlowControlMode::kLeCreditBasedFlowControl;
275 static constexpr ChannelParameters kChannelParams{
276 .mode = kMode,
277 .max_rx_sdu_size = std::nullopt,
278 .flush_timeout = std::nullopt,
279 };
280 static constexpr LECreditBasedConnectionRequestPayload kLeConnReqPayload{
281 .le_psm = 0x0015,
282 .src_cid = DynamicCid(),
283 .mtu = kDefaultMTU,
284 .mps = kMaxInboundPduPayloadSize,
285 .initial_credits = 0x0000,
286 };
287 static constexpr LECreditBasedConnectionResponsePayload kLeConnRspPayload{
288 .dst_cid = DynamicCid(),
289 .mtu = 0x0064,
290 .mps = 0x0032,
291 .initial_credits = 0x0050,
292 .result = LECreditBasedConnectionResult::kSuccess,
293 };
294 static constexpr DisconnectionRequestPayload kDisconnReqPayload{
295 .dst_cid = kLeConnRspPayload.dst_cid,
296 .src_cid = kLeConnReqPayload.src_cid,
297 };
298
299 auto chan =
300 DoOpenOutbound(kLeConnReqPayload, kLeConnRspPayload, kChannelParams);
301 ASSERT_TRUE(chan);
302 EXPECT_TRUE(chan->IsOpen());
303 EXPECT_TRUE(chan->IsConnected());
304 EXPECT_EQ(kLeConnReqPayload.src_cid, chan->local_cid());
305 EXPECT_EQ(kLeConnRspPayload.dst_cid, chan->remote_cid());
306
307 ChannelInfo expected_info = ChannelInfo::MakeCreditBasedFlowControlMode(
308 kMode,
309 kDefaultMTU,
310 kLeConnRspPayload.mtu,
311 kLeConnRspPayload.mps,
312 kLeConnRspPayload.initial_credits);
313 ChannelInfo actual_info = chan->info();
314
315 EXPECT_EQ(expected_info.mode, actual_info.mode);
316 EXPECT_EQ(expected_info.max_rx_sdu_size, actual_info.max_rx_sdu_size);
317 EXPECT_EQ(expected_info.max_tx_sdu_size, actual_info.max_tx_sdu_size);
318 EXPECT_EQ(expected_info.n_frames_in_tx_window,
319 actual_info.n_frames_in_tx_window);
320 EXPECT_EQ(expected_info.max_transmissions, actual_info.max_transmissions);
321 EXPECT_EQ(expected_info.max_tx_pdu_payload_size,
322 actual_info.max_tx_pdu_payload_size);
323 EXPECT_EQ(expected_info.psm, actual_info.psm);
324 EXPECT_EQ(expected_info.flush_timeout, actual_info.flush_timeout);
325 EXPECT_EQ(expected_info.remote_initial_credits,
326 actual_info.remote_initial_credits);
327
328 EXPECT_TRUE(DoCloseOutbound(kDisconnReqPayload));
329 }
330
TEST_F(LeDynamicChannelTest,OpenOutboundSpecificParametersCloseOutbound)331 TEST_F(LeDynamicChannelTest, OpenOutboundSpecificParametersCloseOutbound) {
332 static constexpr auto kMode =
333 CreditBasedFlowControlMode::kLeCreditBasedFlowControl;
334 static constexpr ChannelParameters kChannelParams{
335 .mode = kMode,
336 .max_rx_sdu_size = 0x0023,
337 .flush_timeout = std::nullopt,
338 };
339 static constexpr LECreditBasedConnectionRequestPayload kLeConnReqPayload{
340 .le_psm = 0x0015,
341 .src_cid = DynamicCid(),
342 .mtu = kChannelParams.max_rx_sdu_size.value_or(0),
343 .mps = kMaxInboundPduPayloadSize,
344 .initial_credits = 0x0000,
345 };
346 static constexpr LECreditBasedConnectionResponsePayload kLeConnRspPayload{
347 .dst_cid = DynamicCid(),
348 .mtu = 0x0064,
349 .mps = 0x0032,
350 .initial_credits = 0x0050,
351 .result = LECreditBasedConnectionResult::kSuccess,
352 };
353 static constexpr DisconnectionRequestPayload kDisconnReqPayload{
354 .dst_cid = kLeConnRspPayload.dst_cid,
355 .src_cid = kLeConnReqPayload.src_cid,
356 };
357
358 auto chan =
359 DoOpenOutbound(kLeConnReqPayload, kLeConnRspPayload, kChannelParams);
360 ASSERT_TRUE(chan);
361 EXPECT_TRUE(chan->IsOpen());
362 EXPECT_TRUE(chan->IsConnected());
363 EXPECT_EQ(kLeConnReqPayload.src_cid, chan->local_cid());
364 EXPECT_EQ(kLeConnRspPayload.dst_cid, chan->remote_cid());
365
366 ChannelInfo expected_info = ChannelInfo::MakeCreditBasedFlowControlMode(
367 kMode,
368 *kChannelParams.max_rx_sdu_size,
369 kLeConnRspPayload.mtu,
370 kLeConnRspPayload.mps,
371 kLeConnRspPayload.initial_credits);
372 ChannelInfo actual_info = chan->info();
373
374 EXPECT_EQ(expected_info.mode, actual_info.mode);
375 EXPECT_EQ(expected_info.max_rx_sdu_size, actual_info.max_rx_sdu_size);
376 EXPECT_EQ(expected_info.max_tx_sdu_size, actual_info.max_tx_sdu_size);
377 EXPECT_EQ(expected_info.n_frames_in_tx_window,
378 actual_info.n_frames_in_tx_window);
379 EXPECT_EQ(expected_info.max_transmissions, actual_info.max_transmissions);
380 EXPECT_EQ(expected_info.max_tx_pdu_payload_size,
381 actual_info.max_tx_pdu_payload_size);
382 EXPECT_EQ(expected_info.psm, actual_info.psm);
383 EXPECT_EQ(expected_info.flush_timeout, actual_info.flush_timeout);
384 EXPECT_EQ(expected_info.remote_initial_credits,
385 actual_info.remote_initial_credits);
386
387 EXPECT_TRUE(DoCloseOutbound(kDisconnReqPayload));
388 }
389
TEST_F(LeDynamicChannelTest,OpenOutboundBadChannel)390 TEST_F(LeDynamicChannelTest, OpenOutboundBadChannel) {
391 static constexpr auto kMode =
392 CreditBasedFlowControlMode::kLeCreditBasedFlowControl;
393 static constexpr ChannelParameters kChannelParams{
394 .mode = kMode,
395 .max_rx_sdu_size = std::nullopt,
396 .flush_timeout = std::nullopt,
397 };
398 static constexpr LECreditBasedConnectionRequestPayload kLeConnReqPayload{
399 .le_psm = 0x0015,
400 .src_cid = DynamicCid(),
401 .mtu = kDefaultMTU,
402 .mps = kMaxInboundPduPayloadSize,
403 .initial_credits = 0x0000,
404 };
405 static constexpr LECreditBasedConnectionResponsePayload kLeConnRspPayload{
406 .dst_cid = DynamicCid(-1),
407 .mtu = 0x0064,
408 .mps = 0x0032,
409 .initial_credits = 0x0050,
410 .result = LECreditBasedConnectionResult::kSuccess,
411 };
412
413 auto chan =
414 DoOpenOutbound(kLeConnReqPayload, kLeConnRspPayload, kChannelParams);
415 EXPECT_FALSE(chan);
416 }
417
TEST_F(LeDynamicChannelTest,OpenOutboundRejected)418 TEST_F(LeDynamicChannelTest, OpenOutboundRejected) {
419 static constexpr auto kMode =
420 CreditBasedFlowControlMode::kLeCreditBasedFlowControl;
421 static constexpr ChannelParameters kChannelParams{
422 .mode = kMode,
423 .max_rx_sdu_size = std::nullopt,
424 .flush_timeout = std::nullopt,
425 };
426 static constexpr LECreditBasedConnectionRequestPayload kLeConnReqPayload{
427 .le_psm = 0x0015,
428 .src_cid = DynamicCid(),
429 .mtu = kDefaultMTU,
430 .mps = kMaxInboundPduPayloadSize,
431 .initial_credits = 0x0000,
432 };
433 static constexpr LECreditBasedConnectionResponsePayload kLeConnRspPayload{
434 .dst_cid = DynamicCid(),
435 .mtu = 0x0064,
436 .mps = 0x0032,
437 .initial_credits = 0x0050,
438 .result = LECreditBasedConnectionResult::kSuccess,
439 };
440
441 auto chan = DoOpenOutbound(kLeConnReqPayload,
442 kLeConnRspPayload,
443 kChannelParams,
444 SignalingChannel::Status::kReject);
445 EXPECT_FALSE(chan);
446 }
447
TEST_F(LeDynamicChannelTest,OpenOutboundPsmNotSupported)448 TEST_F(LeDynamicChannelTest, OpenOutboundPsmNotSupported) {
449 static constexpr auto kMode =
450 CreditBasedFlowControlMode::kLeCreditBasedFlowControl;
451 static constexpr ChannelParameters kChannelParams{
452 .mode = kMode,
453 .max_rx_sdu_size = std::nullopt,
454 .flush_timeout = std::nullopt,
455 };
456 static constexpr LECreditBasedConnectionRequestPayload kLeConnReqPayload{
457 .le_psm = 0x0015,
458 .src_cid = DynamicCid(),
459 .mtu = kDefaultMTU,
460 .mps = kMaxInboundPduPayloadSize,
461 .initial_credits = 0x0000,
462 };
463 static constexpr LECreditBasedConnectionResponsePayload kLeConnRspPayload{
464 .dst_cid = DynamicCid(),
465 .mtu = 0x0064,
466 .mps = 0x0032,
467 .initial_credits = 0x0050,
468 .result = LECreditBasedConnectionResult::kPsmNotSupported,
469 };
470
471 auto chan =
472 DoOpenOutbound(kLeConnReqPayload, kLeConnRspPayload, kChannelParams);
473 EXPECT_FALSE(chan);
474 }
475
TEST_F(LeDynamicChannelTest,OpenInboundDefaultParamsCloseInbound)476 TEST_F(LeDynamicChannelTest, OpenInboundDefaultParamsCloseInbound) {
477 static constexpr auto kMode =
478 CreditBasedFlowControlMode::kLeCreditBasedFlowControl;
479 static constexpr Psm kPsm = 0x0015;
480 static constexpr ChannelParameters kChannelParams{
481 .mode = kMode,
482 .max_rx_sdu_size = std::nullopt,
483 .flush_timeout = std::nullopt,
484 };
485 static constexpr LECreditBasedConnectionRequestPayload kLeConnReqPayload{
486 .le_psm = kPsm,
487 .src_cid = DynamicCid(),
488 .mtu = 0x0064,
489 .mps = 0x0032,
490 .initial_credits = 0x0032,
491 };
492 static constexpr LECreditBasedConnectionResponsePayload kLeConnRspPayload{
493 .dst_cid = DynamicCid(),
494 .mtu = kDefaultMTU,
495 .mps = kMaxInboundPduPayloadSize,
496 .initial_credits = 0x0000,
497 .result = LECreditBasedConnectionResult::kSuccess,
498 };
499 static constexpr DisconnectionRequestPayload kDisconnReqPayload{
500 .dst_cid = kLeConnRspPayload.dst_cid,
501 .src_cid = kLeConnReqPayload.src_cid,
502 };
503
504 std::optional<const DynamicChannel*> channel;
505 SetNextInboundChannel(&channel);
506 ASSERT_TRUE(RegisterService(kPsm, kChannelParams));
507 EXPECT_TRUE(DoOpenInbound(kLeConnReqPayload, kLeConnRspPayload));
508 ASSERT_TRUE(channel);
509 ASSERT_TRUE(*channel);
510 EXPECT_TRUE((*channel)->IsOpen());
511 EXPECT_TRUE((*channel)->IsConnected());
512 EXPECT_EQ(kLeConnReqPayload.src_cid, (*channel)->remote_cid());
513 EXPECT_EQ(kLeConnRspPayload.dst_cid, (*channel)->local_cid());
514
515 ChannelInfo expected_info = ChannelInfo::MakeCreditBasedFlowControlMode(
516 kMode,
517 kLeConnRspPayload.mtu,
518 kLeConnReqPayload.mtu,
519 kLeConnReqPayload.mps,
520 kLeConnReqPayload.initial_credits);
521 ChannelInfo actual_info = (*channel)->info();
522
523 EXPECT_EQ(expected_info.mode, actual_info.mode);
524 EXPECT_EQ(expected_info.max_rx_sdu_size, actual_info.max_rx_sdu_size);
525 EXPECT_EQ(expected_info.max_tx_sdu_size, actual_info.max_tx_sdu_size);
526 EXPECT_EQ(expected_info.n_frames_in_tx_window,
527 actual_info.n_frames_in_tx_window);
528 EXPECT_EQ(expected_info.max_transmissions, actual_info.max_transmissions);
529 EXPECT_EQ(expected_info.max_tx_pdu_payload_size,
530 actual_info.max_tx_pdu_payload_size);
531 EXPECT_EQ(expected_info.psm, actual_info.psm);
532 EXPECT_EQ(expected_info.flush_timeout, actual_info.flush_timeout);
533 EXPECT_EQ(expected_info.remote_initial_credits,
534 actual_info.remote_initial_credits);
535
536 EXPECT_TRUE(DoCloseOutbound(kDisconnReqPayload));
537 }
538
TEST_F(LeDynamicChannelTest,OpenInboundUnsupportedPsm)539 TEST_F(LeDynamicChannelTest, OpenInboundUnsupportedPsm) {
540 static constexpr Psm kPsm = 0x0015;
541 static constexpr LECreditBasedConnectionRequestPayload kLeConnReqPayload{
542 .le_psm = kPsm,
543 .src_cid = DynamicCid(),
544 .mtu = 0x0064,
545 .mps = 0x0032,
546 .initial_credits = 0x0032,
547 };
548 static constexpr LECreditBasedConnectionResponsePayload kLeConnRspPayload{
549 .dst_cid = kInvalidChannelId,
550 .mtu = 0,
551 .mps = 0,
552 .initial_credits = 0,
553 .result = LECreditBasedConnectionResult::kPsmNotSupported,
554 };
555
556 std::optional<const DynamicChannel*> channel;
557 SetNextInboundChannel(&channel);
558 EXPECT_TRUE(DoOpenInbound(kLeConnReqPayload, kLeConnRspPayload));
559 EXPECT_FALSE(channel);
560 }
561
TEST_F(LeDynamicChannelTest,OpenInboundBadChannel)562 TEST_F(LeDynamicChannelTest, OpenInboundBadChannel) {
563 static constexpr LECreditBasedConnectionRequestPayload kLeConnReqPayload{
564 .le_psm = 0x0015,
565 .src_cid = DynamicCid(-1),
566 .mtu = 0x0064,
567 .mps = 0x0032,
568 .initial_credits = 0x0050,
569 };
570 static constexpr LECreditBasedConnectionResponsePayload kLeConnRspPayload{
571 .dst_cid = kInvalidChannelId,
572 .mtu = 0,
573 .mps = 0,
574 .initial_credits = 0,
575 .result = LECreditBasedConnectionResult::kInvalidSourceCID,
576 };
577
578 std::optional<const DynamicChannel*> channel;
579 SetNextInboundChannel(&channel);
580 EXPECT_TRUE(DoOpenInbound(kLeConnReqPayload, kLeConnRspPayload));
581 EXPECT_FALSE(channel);
582 }
583
584 } // namespace
585 } // namespace bt::l2cap::internal
586