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/dynamic_channel_registry.h"
16
17 #include "pw_unit_test/framework.h"
18
19 namespace bt::l2cap::internal {
20 namespace {
21
22 constexpr uint16_t kNumChannelsAllowed = 256;
23 constexpr uint16_t kPsm = 0x0001;
24 constexpr ChannelId kRemoteCId = 0x60a3;
25 constexpr ChannelParameters kChannelParams;
26
27 class FakeDynamicChannel final : public DynamicChannel {
28 public:
FakeDynamicChannel(DynamicChannelRegistry * registry,Psm psm,ChannelId local_cid,ChannelId remote_cid)29 FakeDynamicChannel(DynamicChannelRegistry* registry,
30 Psm psm,
31 ChannelId local_cid,
32 ChannelId remote_cid)
33 : DynamicChannel(registry, psm, local_cid, remote_cid) {}
34
35 // DynamicChannel overrides
IsConnected() const36 bool IsConnected() const override { return connected_; }
IsOpen() const37 bool IsOpen() const override { return open_; }
38
info() const39 ChannelInfo info() const override {
40 return ChannelInfo::MakeBasicMode(kDefaultMTU, kDefaultMTU);
41 }
42
DoConnect(ChannelId remote_cid)43 void DoConnect(ChannelId remote_cid) {
44 ASSERT_TRUE(SetRemoteChannelId(remote_cid))
45 << "Could not set non-unique remote_cid " << remote_cid;
46 connected_ = true;
47 }
48
DoOpen(bool new_open=true)49 void DoOpen(bool new_open = true) {
50 open_ = new_open;
51 if (new_open) {
52 set_opened();
53 }
54 open_result_cb_();
55 }
56
DoRemoteClose()57 void DoRemoteClose() {
58 open_ = false;
59 connected_ = false;
60 OnDisconnected();
61 }
62
63 // After calling |set_defer_disconnect_callback|, this returns the callback
64 // passed to |Disconnect|, or an empty callback if |Disconnect| hasn't been
65 // called.
disconnect_done_callback()66 DisconnectDoneCallback& disconnect_done_callback() {
67 return disconnect_done_callback_;
68 }
69
set_defer_disconnect_done_callback()70 void set_defer_disconnect_done_callback() {
71 defer_disconnect_done_callback_ = true;
72 }
73
74 private:
75 // DynamicChannel overrides
Open(fit::closure open_result_cb)76 void Open(fit::closure open_result_cb) override {
77 open_result_cb_ = std::move(open_result_cb);
78 }
Disconnect(DisconnectDoneCallback done_cb)79 void Disconnect(DisconnectDoneCallback done_cb) override {
80 open_ = false;
81 connected_ = false;
82
83 bt_log(DEBUG,
84 "l2cap",
85 "Got Disconnect %#.4x callback: %d",
86 local_cid(),
87 psm());
88
89 ASSERT_FALSE(disconnect_done_callback_);
90 if (defer_disconnect_done_callback_) {
91 disconnect_done_callback_ = std::move(done_cb);
92 } else {
93 done_cb();
94 }
95 }
96
97 fit::closure open_result_cb_;
98 DisconnectDoneCallback disconnect_done_callback_;
99
100 // If true, the Disconnect call does not immediately signal its callback. The
101 // test will have to call it explicitly with |disconnect_callback()|.
102 bool defer_disconnect_done_callback_ = false;
103
104 bool connected_ = false;
105 bool open_ = false;
106 };
107
108 // Fake registry subclass for testing inherited logic. Stubs out |MakeOutbound|
109 // to vend FakeDynamicChannels.
110 class TestDynamicChannelRegistry final : public DynamicChannelRegistry {
111 public:
TestDynamicChannelRegistry(DynamicChannelCallback close_cb,ServiceRequestCallback service_request_cb)112 TestDynamicChannelRegistry(DynamicChannelCallback close_cb,
113 ServiceRequestCallback service_request_cb)
114 : DynamicChannelRegistry(kNumChannelsAllowed,
115 std::move(close_cb),
116 std::move(service_request_cb),
117 /*random_channel_ids=*/true) {}
118
119 // Returns previous channel created.
last_channel()120 FakeDynamicChannel* last_channel() { return last_channel_; }
121
122 // Make public for testing.
123 using DynamicChannelRegistry::AliveChannelCount;
124 using DynamicChannelRegistry::FindAvailableChannelId;
125 using DynamicChannelRegistry::FindChannelByLocalId;
126 using DynamicChannelRegistry::FindChannelByRemoteId;
127 using DynamicChannelRegistry::ForEach;
128 using DynamicChannelRegistry::RequestService;
129
130 private:
131 // DynamicChannelRegistry overrides
MakeOutbound(Psm psm,ChannelId local_cid,ChannelParameters)132 DynamicChannelPtr MakeOutbound(Psm psm,
133 ChannelId local_cid,
134 ChannelParameters) override {
135 return MakeChannelInternal(psm, local_cid, kInvalidChannelId);
136 }
137
MakeInbound(Psm psm,ChannelId local_cid,ChannelId remote_cid,ChannelParameters)138 DynamicChannelPtr MakeInbound(Psm psm,
139 ChannelId local_cid,
140 ChannelId remote_cid,
141 ChannelParameters) override {
142 auto channel = MakeChannelInternal(psm, local_cid, remote_cid);
143 channel->DoConnect(remote_cid);
144 return channel;
145 }
146
MakeChannelInternal(Psm psm,ChannelId local_cid,ChannelId remote_cid)147 std::unique_ptr<FakeDynamicChannel> MakeChannelInternal(
148 Psm psm, ChannelId local_cid, ChannelId remote_cid) {
149 auto channel =
150 std::make_unique<FakeDynamicChannel>(this, psm, local_cid, remote_cid);
151 last_channel_ = channel.get();
152 return channel;
153 }
154
155 FakeDynamicChannel* last_channel_ = nullptr;
156 };
157
158 // DynamicChannelCallback static handler
DoNothing(const DynamicChannel *)159 void DoNothing(const DynamicChannel*) {}
160
161 // ServiceRequestCallback static handler
RejectAllServices(Psm)162 std::optional<DynamicChannelRegistry::ServiceInfo> RejectAllServices(
163 Psm /*psm*/) {
164 return std::nullopt;
165 }
166
TEST(DynamicChannelRegistryTest,OpenAndRemoteCloseChannel)167 TEST(DynamicChannelRegistryTest, OpenAndRemoteCloseChannel) {
168 ChannelId local_cid = kInvalidChannelId;
169 ChannelId remote_cid = kInvalidChannelId;
170 bool close_cb_called = false;
171 auto close_cb = [&](const DynamicChannel* chan) {
172 EXPECT_FALSE(close_cb_called);
173 close_cb_called = true;
174 EXPECT_TRUE(chan);
175 EXPECT_FALSE(chan->IsConnected());
176 EXPECT_FALSE(chan->IsOpen());
177 EXPECT_EQ(local_cid, chan->local_cid());
178 EXPECT_EQ(remote_cid, chan->remote_cid());
179 };
180
181 TestDynamicChannelRegistry registry(std::move(close_cb), RejectAllServices);
182 EXPECT_NE(kInvalidChannelId, registry.FindAvailableChannelId());
183
184 bool open_result_cb_called = false;
185 auto open_result_cb = [&](const DynamicChannel* chan) {
186 EXPECT_FALSE(open_result_cb_called);
187 open_result_cb_called = true;
188 EXPECT_TRUE(chan);
189 EXPECT_EQ(kPsm, chan->psm());
190 local_cid = chan->local_cid();
191 remote_cid = chan->remote_cid();
192 };
193
194 registry.OpenOutbound(kPsm, kChannelParams, std::move(open_result_cb));
195 registry.last_channel()->DoConnect(kRemoteCId);
196 registry.last_channel()->DoOpen();
197
198 EXPECT_TRUE(open_result_cb_called);
199 EXPECT_FALSE(close_cb_called);
200 auto channel_by_local_id = registry.FindChannelByLocalId(local_cid);
201 auto channel_by_remote_id = registry.FindChannelByRemoteId(remote_cid);
202 EXPECT_TRUE(channel_by_local_id);
203 EXPECT_TRUE(channel_by_remote_id);
204 EXPECT_EQ(channel_by_local_id, channel_by_remote_id);
205
206 registry.last_channel()->DoRemoteClose();
207 EXPECT_TRUE(close_cb_called);
208 EXPECT_FALSE(registry.FindChannelByLocalId(local_cid));
209 EXPECT_FALSE(registry.FindChannelByRemoteId(remote_cid));
210 }
211
TEST(DynamicChannelRegistryTest,OpenAndLocalCloseChannel)212 TEST(DynamicChannelRegistryTest, OpenAndLocalCloseChannel) {
213 bool registry_close_cb_called = false;
214 auto registry_close_cb = [&](const DynamicChannel*) {
215 registry_close_cb_called = true;
216 };
217
218 TestDynamicChannelRegistry registry(std::move(registry_close_cb),
219 RejectAllServices);
220
221 bool open_result_cb_called = false;
222 ChannelId local_cid = kInvalidChannelId;
223 auto open_result_cb = [&](const DynamicChannel* chan) {
224 EXPECT_FALSE(open_result_cb_called);
225 open_result_cb_called = true;
226 EXPECT_TRUE(chan);
227 local_cid = chan->local_cid();
228 };
229
230 registry.OpenOutbound(kPsm, kChannelParams, std::move(open_result_cb));
231 registry.last_channel()->DoConnect(kRemoteCId);
232 registry.last_channel()->DoOpen();
233
234 EXPECT_TRUE(open_result_cb_called);
235 EXPECT_TRUE(registry.FindChannelByLocalId(local_cid));
236
237 bool close_cb_called = false;
238 registry.CloseChannel(local_cid, [&] { close_cb_called = true; });
239 EXPECT_FALSE(registry_close_cb_called);
240 EXPECT_TRUE(close_cb_called);
241 EXPECT_FALSE(registry.FindChannelByLocalId(local_cid));
242 }
243
TEST(DynamicChannelRegistryTest,RejectServiceRequest)244 TEST(DynamicChannelRegistryTest, RejectServiceRequest) {
245 bool service_request_cb_called = false;
246 auto service_request_cb = [&service_request_cb_called](Psm psm) {
247 EXPECT_FALSE(service_request_cb_called);
248 EXPECT_EQ(kPsm, psm);
249 service_request_cb_called = true;
250 return std::nullopt;
251 };
252
253 TestDynamicChannelRegistry registry(DoNothing, std::move(service_request_cb));
254
255 registry.RequestService(kPsm, registry.FindAvailableChannelId(), kRemoteCId);
256 EXPECT_TRUE(service_request_cb_called);
257 EXPECT_FALSE(registry.last_channel());
258 }
259
TEST(DynamicChannelRegistryTest,AcceptServiceRequestThenOpenOk)260 TEST(DynamicChannelRegistryTest, AcceptServiceRequestThenOpenOk) {
261 bool open_result_cb_called = false;
262 ChannelId local_cid = kInvalidChannelId;
263 ChannelId remote_cid = kInvalidChannelId;
264 DynamicChannelRegistry::DynamicChannelCallback open_result_cb =
265 [&](const DynamicChannel* chan) {
266 EXPECT_FALSE(open_result_cb_called);
267 open_result_cb_called = true;
268 EXPECT_TRUE(chan);
269 EXPECT_EQ(kPsm, chan->psm());
270 local_cid = chan->local_cid();
271 remote_cid = chan->remote_cid();
272 };
273
274 bool service_request_cb_called = false;
275 auto service_request_cb = [&service_request_cb_called,
276 open_result_cb =
277 std::move(open_result_cb)](Psm psm) mutable {
278 EXPECT_FALSE(service_request_cb_called);
279 EXPECT_EQ(kPsm, psm);
280 service_request_cb_called = true;
281 return DynamicChannelRegistry::ServiceInfo{ChannelParameters(),
282 open_result_cb.share()};
283 };
284
285 TestDynamicChannelRegistry registry(DoNothing, std::move(service_request_cb));
286
287 registry.RequestService(kPsm, registry.FindAvailableChannelId(), kRemoteCId);
288 EXPECT_TRUE(service_request_cb_called);
289 ASSERT_TRUE(registry.last_channel());
290 registry.last_channel()->DoOpen();
291
292 EXPECT_TRUE(open_result_cb_called);
293 EXPECT_NE(kInvalidChannelId, local_cid);
294 EXPECT_NE(kInvalidChannelId, remote_cid);
295 EXPECT_TRUE(registry.FindChannelByLocalId(local_cid));
296
297 bool close_cb_called = false;
298 registry.CloseChannel(local_cid, [&] { close_cb_called = true; });
299 EXPECT_TRUE(close_cb_called);
300 EXPECT_FALSE(registry.FindChannelByLocalId(local_cid));
301 }
302
TEST(DynamicChannelRegistryTest,AcceptServiceRequestThenOpenFails)303 TEST(DynamicChannelRegistryTest, AcceptServiceRequestThenOpenFails) {
304 bool open_result_cb_called = false;
305 DynamicChannelRegistry::DynamicChannelCallback open_result_cb =
306 [&open_result_cb_called](const DynamicChannel*) {
307 open_result_cb_called = true;
308 };
309
310 bool service_request_cb_called = false;
311 auto service_request_cb = [&service_request_cb_called,
312 open_result_cb =
313 std::move(open_result_cb)](Psm psm) mutable {
314 EXPECT_FALSE(service_request_cb_called);
315 EXPECT_EQ(kPsm, psm);
316 service_request_cb_called = true;
317 return DynamicChannelRegistry::ServiceInfo{ChannelParameters(),
318 open_result_cb.share()};
319 };
320
321 TestDynamicChannelRegistry registry(DoNothing, std::move(service_request_cb));
322
323 ChannelId local_cid = registry.FindAvailableChannelId();
324 EXPECT_NE(kInvalidChannelId, local_cid);
325 registry.RequestService(kPsm, local_cid, kRemoteCId);
326 EXPECT_TRUE(service_request_cb_called);
327 ASSERT_TRUE(registry.last_channel());
328 registry.last_channel()->DoOpen(false);
329
330 // Don't get channels that failed to open.
331 EXPECT_FALSE(open_result_cb_called);
332 EXPECT_FALSE(registry.FindChannelByLocalId(local_cid));
333
334 // The channel should be released upon this failure.
335 EXPECT_EQ(0u, registry.AliveChannelCount());
336 }
337
TEST(DynamicChannelRegistryTest,DestroyRegistryWithOpenChannelNoDisconnectionRequest)338 TEST(DynamicChannelRegistryTest,
339 DestroyRegistryWithOpenChannelNoDisconnectionRequest) {
340 bool close_cb_called = false;
341 auto close_cb = [&close_cb_called](const DynamicChannel*) {
342 close_cb_called = true;
343 };
344
345 TestDynamicChannelRegistry registry(std::move(close_cb), RejectAllServices);
346
347 bool open_result_cb_called = false;
348 auto open_result_cb = [&open_result_cb_called](const DynamicChannel* chan) {
349 EXPECT_FALSE(open_result_cb_called);
350 open_result_cb_called = true;
351 EXPECT_TRUE(chan);
352 };
353
354 registry.OpenOutbound(kPsm, kChannelParams, std::move(open_result_cb));
355 registry.last_channel()->DoConnect(kRemoteCId);
356 registry.last_channel()->DoOpen();
357
358 EXPECT_TRUE(open_result_cb_called);
359 EXPECT_TRUE(registry.FindChannelByRemoteId(kRemoteCId));
360 EXPECT_FALSE(close_cb_called);
361 }
362
TEST(DynamicChannelRegistryTest,ErrorConnectingChannel)363 TEST(DynamicChannelRegistryTest, ErrorConnectingChannel) {
364 bool open_result_cb_called = false;
365 auto open_result_cb = [&open_result_cb_called](const DynamicChannel* chan) {
366 EXPECT_FALSE(open_result_cb_called);
367 open_result_cb_called = true;
368 EXPECT_FALSE(chan);
369 };
370 bool close_cb_called = false;
371 auto close_cb = [&close_cb_called](auto) { close_cb_called = true; };
372
373 TestDynamicChannelRegistry registry(std::move(close_cb), RejectAllServices);
374
375 registry.OpenOutbound(kPsm, kChannelParams, std::move(open_result_cb));
376 registry.last_channel()->DoOpen(false);
377
378 EXPECT_TRUE(open_result_cb_called);
379 EXPECT_FALSE(close_cb_called);
380
381 // Should be no alive channels anymore.
382 EXPECT_EQ(0u, registry.AliveChannelCount());
383 }
384
TEST(DynamicChannelRegistryTest,ExhaustedChannelIds)385 TEST(DynamicChannelRegistryTest, ExhaustedChannelIds) {
386 int open_result_cb_count = 0;
387
388 // This callback expects the channel to be creatable.
389 DynamicChannelRegistry::DynamicChannelCallback success_open_result_cb =
390 [&open_result_cb_count](const DynamicChannel* chan) {
391 ASSERT_NE(nullptr, chan);
392 EXPECT_NE(kInvalidChannelId, chan->local_cid());
393 open_result_cb_count++;
394 };
395
396 int close_cb_count = 0;
397 auto close_cb = [&close_cb_count](auto) { close_cb_count++; };
398
399 TestDynamicChannelRegistry registry(std::move(close_cb), RejectAllServices);
400
401 // Open a lot of channels.
402 for (ChannelId i = 0; i < kNumChannelsAllowed; i++) {
403 registry.OpenOutbound(
404 kPsm + i, kChannelParams, success_open_result_cb.share());
405 registry.last_channel()->DoConnect(kRemoteCId + i);
406 registry.last_channel()->DoOpen();
407 }
408 EXPECT_EQ(kNumChannelsAllowed, open_result_cb_count);
409 EXPECT_EQ(0, close_cb_count);
410
411 // Ensure that channel IDs are exhausted.
412 EXPECT_EQ(kInvalidChannelId, registry.FindAvailableChannelId());
413
414 // This callback expects the channel to fail creation.
415 auto fail_open_result_cb =
416 [&open_result_cb_count](const DynamicChannel* chan) {
417 EXPECT_FALSE(chan);
418 open_result_cb_count++;
419 };
420
421 // Try to open a new channel.
422 registry.OpenOutbound(kPsm, kChannelParams, std::move(fail_open_result_cb));
423 EXPECT_EQ(kNumChannelsAllowed + 1, open_result_cb_count);
424 EXPECT_EQ(0, close_cb_count);
425
426 // Close the most recently opened channel.
427 auto last_remote_cid = registry.last_channel()->remote_cid();
428 registry.last_channel()->DoRemoteClose();
429 EXPECT_EQ(1, close_cb_count);
430 EXPECT_NE(kInvalidChannelId, registry.FindAvailableChannelId());
431
432 // Try to open a channel again.
433 registry.OpenOutbound(kPsm, kChannelParams, success_open_result_cb.share());
434 registry.last_channel()->DoConnect(last_remote_cid);
435 registry.last_channel()->DoOpen();
436 EXPECT_EQ(kNumChannelsAllowed + 2, open_result_cb_count);
437 EXPECT_EQ(1, close_cb_count);
438
439 ChannelId last_local_cid = registry.last_channel()->local_cid();
440
441 int close_cb_called = 0;
442 for (ChannelId i = 0; i < kNumChannelsAllowed; i++) {
443 registry.CloseChannel(kFirstDynamicChannelId + i,
444 [&] { close_cb_called++; });
445 }
446 EXPECT_EQ(close_cb_called, kNumChannelsAllowed);
447 EXPECT_FALSE(registry.FindChannelByLocalId(last_local_cid));
448 }
449
TEST(DynamicChannelRegistryTest,ChannelIdNotReusedUntilDisconnectionCompletes)450 TEST(DynamicChannelRegistryTest,
451 ChannelIdNotReusedUntilDisconnectionCompletes) {
452 TestDynamicChannelRegistry registry(DoNothing, RejectAllServices);
453
454 // This callback expects the channel to be creatable.
455 int open_result_cb_count = 0;
456 DynamicChannelRegistry::DynamicChannelCallback success_open_result_cb =
457 [&](const DynamicChannel* chan) {
458 ASSERT_NE(nullptr, chan);
459 EXPECT_NE(kInvalidChannelId, chan->local_cid());
460 open_result_cb_count++;
461 };
462
463 // Open all but one of the available channels.
464 for (ChannelId i = 0; i < kNumChannelsAllowed - 1; i++) {
465 registry.OpenOutbound(
466 kPsm + i, kChannelParams, success_open_result_cb.share());
467 registry.last_channel()->DoConnect(kRemoteCId + i);
468 registry.last_channel()->DoOpen();
469 }
470 EXPECT_EQ(kNumChannelsAllowed - 1, open_result_cb_count);
471
472 // This callback records the info on channel that was created
473 ChannelId last_local_cid = kInvalidChannelId;
474 auto record_open_result_cb = [&](const DynamicChannel* chan) {
475 ASSERT_TRUE(chan);
476 last_local_cid = chan->local_cid();
477 };
478
479 // Ensure that channel IDs are not exhausted.
480 EXPECT_NE(kInvalidChannelId, registry.FindAvailableChannelId());
481
482 // Open a the last available channel.
483 registry.OpenOutbound(kPsm, kChannelParams, std::move(record_open_result_cb));
484
485 registry.last_channel()->DoConnect(kRemoteCId + kNumChannelsAllowed - 1);
486 registry.last_channel()->DoOpen();
487
488 EXPECT_NE(kInvalidChannelId, last_local_cid);
489 ASSERT_TRUE(registry.FindChannelByLocalId(last_local_cid));
490
491 // The channels are exhausted now.
492 ASSERT_EQ(kInvalidChannelId, registry.FindAvailableChannelId());
493
494 // Close the channel but don't let the disconnection complete.
495 FakeDynamicChannel* const last_channel = registry.last_channel();
496 last_channel->set_defer_disconnect_done_callback();
497 int close_cb_called = 0;
498 registry.CloseChannel(last_local_cid, [&] { close_cb_called++; });
499
500 // New channels should not reuse the "mostly disconnected" channel's ID.
501 EXPECT_EQ(close_cb_called, 0);
502 // There should still be no channels left.
503 EXPECT_EQ(kInvalidChannelId, registry.FindAvailableChannelId());
504 ASSERT_TRUE(registry.FindChannelByLocalId(last_local_cid));
505 EXPECT_FALSE(registry.FindChannelByLocalId(last_local_cid)->IsConnected());
506
507 // Complete the disconnection for the first channel opened.
508 ASSERT_TRUE(last_channel->disconnect_done_callback());
509 last_channel->disconnect_done_callback()();
510 EXPECT_EQ(close_cb_called, 1);
511 EXPECT_EQ(last_local_cid, registry.FindAvailableChannelId());
512
513 // Open a new channel and make sure that last ID can be reused now.
514 bool open_result_cb_called = false;
515 auto open_result_cb = [&](const DynamicChannel* chan) {
516 EXPECT_FALSE(open_result_cb_called);
517 open_result_cb_called = true;
518 ASSERT_TRUE(chan);
519 EXPECT_EQ(last_local_cid, chan->local_cid());
520 };
521 registry.OpenOutbound(kPsm, kChannelParams, std::move(open_result_cb));
522 registry.last_channel()->DoConnect(kRemoteCId + kNumChannelsAllowed - 1);
523 registry.last_channel()->DoOpen();
524 EXPECT_TRUE(open_result_cb_called);
525
526 close_cb_called = 0;
527 for (ChannelId i = 0; i < kNumChannelsAllowed; i++) {
528 registry.CloseChannel(kFirstDynamicChannelId + i,
529 [&] { close_cb_called++; });
530 }
531 EXPECT_EQ(close_cb_called, kNumChannelsAllowed);
532 EXPECT_FALSE(registry.FindChannelByLocalId(last_local_cid));
533 }
534
535 // Removing a channel from the channel map while iterating the channels in
536 // ForEach should not cause a use-after-free of the invalidated pointer.
TEST(DynamicChannelRegistryTest,CloseChannelInForEachCallback)537 TEST(DynamicChannelRegistryTest, CloseChannelInForEachCallback) {
538 bool registry_close_cb_called = false;
539 auto registry_close_cb = [&](const DynamicChannel*) {
540 registry_close_cb_called = true;
541 };
542
543 TestDynamicChannelRegistry registry(std::move(registry_close_cb),
544 RejectAllServices);
545
546 bool open_result_cb_called = false;
547 ChannelId local_cid = kInvalidChannelId;
548 auto open_result_cb = [&](const DynamicChannel* chan) {
549 EXPECT_FALSE(open_result_cb_called);
550 open_result_cb_called = true;
551 EXPECT_TRUE(chan);
552 local_cid = chan->local_cid();
553 };
554
555 registry.OpenOutbound(kPsm, kChannelParams, std::move(open_result_cb));
556 registry.last_channel()->DoConnect(kRemoteCId);
557 registry.last_channel()->DoOpen();
558
559 EXPECT_TRUE(open_result_cb_called);
560 EXPECT_TRUE(registry.FindChannelByLocalId(local_cid));
561
562 // Even if the next iterator is "end", it would still be unsafe to advance the
563 // erased iterator.
564 registry.ForEach([&](DynamicChannel* chan) {
565 registry.CloseChannel(chan->local_cid(), [] {});
566 });
567
568 EXPECT_FALSE(registry.FindChannelByLocalId(local_cid));
569 }
570
571 } // namespace
572 } // namespace bt::l2cap::internal
573