| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/fake_dynamic_channel.h" |
| |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/byte_buffer.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/test_packets.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/fake_l2cap.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/fake_signaling_server.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/test_helpers.h" |
| |
| namespace bt::testing { |
| namespace { |
| |
| hci_spec::ConnectionHandle kConnectionHandle = 0x01; |
| l2cap::CommandId kCommandId = 0x02; |
| l2cap::Psm kPsm = l2cap::kSDP; |
| |
| TEST(FakeDynamicChannelTest, ConnectOpenDisconnectChannel) { |
| std::unique_ptr<ByteBuffer> received_packet; |
| auto send_cb = [&received_packet]( |
| auto kConnectionHandle, auto cid, auto& buffer) { |
| received_packet = std::make_unique<DynamicByteBuffer>(buffer); |
| }; |
| auto fake_l2cap = FakeL2cap(send_cb); |
| auto server = std::make_unique<FakeSignalingServer>(); |
| server->RegisterWithL2cap(&fake_l2cap); |
| auto channel_cb = [](auto fake_dynamic_channel) {}; |
| fake_l2cap.RegisterService(kPsm, channel_cb); |
| l2cap::ChannelId src_id = l2cap::kFirstDynamicChannelId; |
| l2cap::ChannelParameters params; |
| |
| // Assemble and send the ConnectionRequest to connect, but not open, the |
| // channel. |
| auto connection_acl_packet = l2cap::testing::AclConnectionReq( |
| kCommandId, kConnectionHandle, src_id, kPsm); |
| const auto& connection_header = |
| connection_acl_packet.To<hci_spec::ACLDataHeader>(); |
| auto connection_header_len = sizeof(connection_header); |
| auto connection_payload_len = le16toh(connection_header.data_total_length); |
| auto connection_packet = DynamicByteBuffer(connection_payload_len); |
| connection_acl_packet.Copy( |
| &connection_packet, connection_header_len, connection_payload_len); |
| fake_l2cap.HandlePdu(kConnectionHandle, connection_packet); |
| |
| // Anticipate that we then receive a ConfigurationRequest. HandlePdu will |
| // first send a ConnectionResponse, but the most recent packet should be a |
| // ConfigurationRequest. The channel should also be connected, but not open, |
| // at this time. |
| // Manually create the expected ConfigurationRequest with no payload. |
| StaticByteBuffer expected_request( |
| // Configuration request command code, CommandId associated with the test |
| l2cap::kConfigurationRequest, |
| kCommandId, |
| // Payload length (4 total bytes) |
| 0x04, |
| 0x00, |
| // Source ID (2 bytes) |
| LowerBits(src_id), |
| UpperBits(src_id), |
| // No continuation flags (2 bytes) |
| 0x00, |
| 0x00); |
| EXPECT_TRUE(ContainersEqual(expected_request, *received_packet)); |
| EXPECT_FALSE( |
| fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| ->configuration_request_received()); |
| EXPECT_FALSE( |
| fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| ->configuration_response_received()); |
| EXPECT_FALSE( |
| fake_l2cap.FindDynamicChannelByRemoteId(kConnectionHandle, src_id) |
| ->opened()); |
| |
| // Send a ConfigurationResponse to the received ConfigurationRequest. |
| auto configuration_response_acl_packet = l2cap::testing::AclConfigRsp( |
| kCommandId, kConnectionHandle, src_id, params); |
| const auto& configuration_response_header = |
| configuration_response_acl_packet.To<hci_spec::ACLDataHeader>(); |
| auto configuration_response_header_len = |
| sizeof(configuration_response_header); |
| auto configuration_response_payload_len = |
| le16toh(configuration_response_header.data_total_length); |
| auto configuration_response_packet = |
| DynamicByteBuffer(configuration_response_payload_len); |
| configuration_response_acl_packet.Copy(&configuration_response_packet, |
| configuration_response_header_len, |
| configuration_response_payload_len); |
| fake_l2cap.HandlePdu(kConnectionHandle, configuration_response_packet); |
| EXPECT_FALSE( |
| fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| ->configuration_request_received()); |
| EXPECT_TRUE(fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| ->configuration_response_received()); |
| EXPECT_FALSE( |
| fake_l2cap.FindDynamicChannelByRemoteId(kConnectionHandle, src_id) |
| ->opened()); |
| |
| // Assemble and send the ConfigurationRequest to open up the channel. |
| // In this isolated test, we can assume that the src_id and dest_id are |
| // identical. |
| auto configuration_request_acl_packet = l2cap::testing::AclConfigReq( |
| kCommandId, kConnectionHandle, src_id, params); |
| const auto& configuration_request_header = |
| configuration_request_acl_packet.To<hci_spec::ACLDataHeader>(); |
| auto configuration_request_header_len = sizeof(configuration_request_header); |
| auto configuration_request_payload_len = |
| le16toh(configuration_request_header.data_total_length); |
| auto configuration_request_packet = |
| DynamicByteBuffer(configuration_request_payload_len); |
| configuration_request_acl_packet.Copy(&configuration_request_packet, |
| configuration_request_header_len, |
| configuration_request_payload_len); |
| fake_l2cap.HandlePdu(kConnectionHandle, configuration_request_packet); |
| |
| // Anticipate that we then receive a ConfigurationResponse after we send a |
| // Manually create the expected ConfigurationRequest with no payload. |
| StaticByteBuffer expected_response( |
| // Configuration request command code, CommandId associated with the test |
| l2cap::kConfigurationResponse, |
| kCommandId, |
| // Payload length (6 total bytes) |
| 0x06, |
| 0x00, |
| // Source ID (2 bytes) |
| LowerBits(src_id), |
| UpperBits(src_id), |
| // No continuation flags (2 bytes) |
| 0x00, |
| 0x00, |
| // Result (Success) |
| LowerBits(0x0000), |
| UpperBits(0x0000)); |
| EXPECT_TRUE(ContainersEqual(expected_response, *received_packet)); |
| EXPECT_TRUE(fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| ->configuration_request_received()); |
| EXPECT_TRUE(fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| ->configuration_response_received()); |
| EXPECT_TRUE( |
| fake_l2cap.FindDynamicChannelByRemoteId(kConnectionHandle, src_id) |
| ->opened()); |
| |
| // Assemble and send the DisconnectionRequest to open up the channel. |
| // In this isolated test, we can assume that the src_id and dest_id are |
| // identical. |
| auto disconnection_acl_packet = l2cap::testing::AclDisconnectionReq( |
| kCommandId, kConnectionHandle, src_id, src_id); |
| const auto& disconnection_header = |
| disconnection_acl_packet.To<hci_spec::ACLDataHeader>(); |
| auto disconnection_header_len = sizeof(disconnection_header); |
| auto disconnection_payload_len = |
| le16toh(disconnection_header.data_total_length); |
| auto disconnection_packet = DynamicByteBuffer(disconnection_payload_len); |
| disconnection_acl_packet.Copy(&disconnection_packet, |
| disconnection_header_len, |
| disconnection_payload_len); |
| fake_l2cap.HandlePdu(kConnectionHandle, disconnection_packet); |
| |
| // Anticipate that we receive a DisconnectionResponse after we send the |
| // request and that the channel has been deleted. |
| StaticByteBuffer disconnection_response( |
| // Configuration request command code, CommandId associated with the test |
| l2cap::kDisconnectionResponse, |
| kCommandId, |
| // Payload length (4 total bytes) |
| 0x04, |
| 0x00, |
| // Source ID (2 bytes) |
| LowerBits(src_id), |
| UpperBits(src_id), |
| // Dest ID (2 bytes) |
| LowerBits(src_id), |
| UpperBits(src_id)); |
| EXPECT_TRUE(ContainersEqual(disconnection_response, *received_packet)); |
| EXPECT_FALSE(fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| .is_alive()); |
| } |
| |
| TEST(FakeDynamicChannelTest, FailToRegisterChannelWithoutRegisteredService) { |
| // Create a custom FakeL2cap with no registered services. |
| std::unique_ptr<ByteBuffer> received_packet; |
| auto send_cb = [&received_packet]( |
| auto kConnectionHandle, auto cid, auto& buffer) { |
| received_packet = std::make_unique<DynamicByteBuffer>(buffer); |
| }; |
| auto fake_l2cap_without_service = FakeL2cap(send_cb); |
| auto server = std::make_unique<FakeSignalingServer>(); |
| server->RegisterWithL2cap(&fake_l2cap_without_service); |
| l2cap::ChannelId src_id = l2cap::kFirstDynamicChannelId; |
| |
| // Assemble and send the ConnectionRequest to connect, but not open, the |
| // channel. |
| auto connection_acl_packet = l2cap::testing::AclConnectionReq( |
| kCommandId, kConnectionHandle, src_id, kPsm); |
| const auto& connection_header = |
| connection_acl_packet.To<hci_spec::ACLDataHeader>(); |
| auto connection_header_len = sizeof(connection_header); |
| auto connection_payload_len = le16toh(connection_header.data_total_length); |
| auto connection_packet = DynamicByteBuffer(connection_payload_len); |
| connection_acl_packet.Copy( |
| &connection_packet, connection_header_len, connection_payload_len); |
| fake_l2cap_without_service.HandlePdu(kConnectionHandle, connection_packet); |
| |
| // Anticipate that we will receive a rejection as the packet is not supported. |
| // As this is an isolated test case, assume that src_id and dst_id are the |
| // same. |
| auto expected_acl_response = l2cap::testing::AclConnectionRsp( |
| kCommandId, |
| kConnectionHandle, |
| src_id, |
| l2cap::kInvalidChannelId, |
| l2cap::ConnectionResult::kPsmNotSupported); |
| auto expected_response = expected_acl_response.view( |
| sizeof(hci_spec::ACLDataHeader) + sizeof(l2cap::CommandHeader)); |
| EXPECT_TRUE(ContainersEqual(expected_response, *received_packet)); |
| EXPECT_FALSE(fake_l2cap_without_service |
| .FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| .is_alive()); |
| } |
| |
| TEST(FakeDynamicChannelTest, FailToRegisterChannelWithInvalidCid) { |
| // Configure FakeSignalingServer to copy any received signaling packets. |
| std::unique_ptr<ByteBuffer> received_packet; |
| auto send_cb = [&received_packet]( |
| auto kConnectionHandle, auto cid, auto& buffer) { |
| received_packet = std::make_unique<DynamicByteBuffer>(buffer); |
| }; |
| auto fake_l2cap = FakeL2cap(send_cb); |
| auto server = std::make_unique<FakeSignalingServer>(); |
| server->RegisterWithL2cap(&fake_l2cap); |
| auto channel_cb = [](auto fake_dynamic_channel) {}; |
| fake_l2cap.RegisterService(kPsm, channel_cb); |
| l2cap::ChannelId src_id = l2cap::kInvalidChannelId; |
| |
| // Assemble and send the ConnectionRequest to connect, but not open, the |
| // channel. |
| auto connection_acl_packet = l2cap::testing::AclConnectionReq( |
| kCommandId, kConnectionHandle, src_id, kPsm); |
| const auto& connection_header = |
| connection_acl_packet.To<hci_spec::ACLDataHeader>(); |
| auto connection_header_len = sizeof(connection_header); |
| auto connection_payload_len = le16toh(connection_header.data_total_length); |
| auto connection_packet = DynamicByteBuffer(connection_payload_len); |
| connection_acl_packet.Copy( |
| &connection_packet, connection_header_len, connection_payload_len); |
| fake_l2cap.HandlePdu(kConnectionHandle, connection_packet); |
| |
| // Anticipate that we will receive a rejection as the ID is not supported. |
| auto expected_acl_response = l2cap::testing::AclConnectionRsp( |
| kCommandId, |
| kConnectionHandle, |
| src_id, |
| l2cap::kInvalidChannelId, |
| l2cap::ConnectionResult::kInvalidSourceCID); |
| auto expected_response = expected_acl_response.view( |
| sizeof(hci_spec::ACLDataHeader) + sizeof(l2cap::CommandHeader)); |
| EXPECT_TRUE(ContainersEqual(expected_response, *received_packet)); |
| EXPECT_FALSE(fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| .is_alive()); |
| } |
| |
| TEST(FakeDynamicChannelTest, FailToRegisterDuplicateRemoteId) { |
| std::unique_ptr<ByteBuffer> received_packet; |
| auto send_cb = [&received_packet]( |
| auto kConnectionHandle, auto cid, auto& buffer) { |
| received_packet = std::make_unique<DynamicByteBuffer>(buffer); |
| }; |
| auto fake_l2cap = FakeL2cap(send_cb); |
| auto server = std::make_unique<FakeSignalingServer>(); |
| server->RegisterWithL2cap(&fake_l2cap); |
| auto channel_cb = [](auto fake_dynamic_channel) {}; |
| fake_l2cap.RegisterService(kPsm, channel_cb); |
| l2cap::ChannelId src_id = l2cap::kFirstDynamicChannelId; |
| l2cap::ChannelParameters params; |
| |
| // Assemble and send the ConnectionRequest to connect, but not open, the |
| // channel. |
| auto connection_acl_packet = l2cap::testing::AclConnectionReq( |
| kCommandId, kConnectionHandle, src_id, kPsm); |
| const auto& connection_header = |
| connection_acl_packet.To<hci_spec::ACLDataHeader>(); |
| auto connection_header_len = sizeof(connection_header); |
| auto connection_payload_len = le16toh(connection_header.data_total_length); |
| auto connection_packet = DynamicByteBuffer(connection_payload_len); |
| connection_acl_packet.Copy( |
| &connection_packet, connection_header_len, connection_payload_len); |
| fake_l2cap.HandlePdu(kConnectionHandle, connection_packet); |
| |
| // Anticipate that we then receive a ConfigurationRequest. HandlePdu will |
| // first send a ConnectionResponse, but the most recent packet should be a |
| // ConfigurationRequest. The channel should also be connected, but not open, |
| // at this time. |
| // Manually create the expected ConfigurationRequest with no payload. |
| StaticByteBuffer expected_request( |
| // Configuration request command code, CommandId associated with the test |
| l2cap::kConfigurationRequest, |
| kCommandId, |
| // Payload length (4 total bytes) |
| 0x04, |
| 0x00, |
| // Source ID (2 bytes) |
| LowerBits(src_id), |
| UpperBits(src_id), |
| // No continuation flags (2 bytes) |
| 0x00, |
| 0x00); |
| EXPECT_TRUE(ContainersEqual(expected_request, *received_packet)); |
| EXPECT_FALSE( |
| fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| ->configuration_request_received()); |
| EXPECT_FALSE( |
| fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| ->configuration_response_received()); |
| EXPECT_FALSE( |
| fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| ->opened()); |
| |
| // Send a ConfigurationResponse to the received ConfigurationRequest. |
| auto configuration_response_acl_packet = l2cap::testing::AclConfigRsp( |
| kCommandId, kConnectionHandle, src_id, params); |
| const auto& configuration_response_header = |
| configuration_response_acl_packet.To<hci_spec::ACLDataHeader>(); |
| auto configuration_response_header_len = |
| sizeof(configuration_response_header); |
| auto configuration_response_payload_len = |
| le16toh(configuration_response_header.data_total_length); |
| auto configuration_response_packet = |
| DynamicByteBuffer(configuration_response_payload_len); |
| configuration_response_acl_packet.Copy(&configuration_response_packet, |
| configuration_response_header_len, |
| configuration_response_payload_len); |
| fake_l2cap.HandlePdu(kConnectionHandle, configuration_response_packet); |
| EXPECT_FALSE( |
| fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| ->configuration_request_received()); |
| EXPECT_TRUE(fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| ->configuration_response_received()); |
| EXPECT_FALSE( |
| fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| ->opened()); |
| |
| // Assemble and send the ConfigurationRequest to open up the channel. |
| // In this isolated test, we can assume that the src_id and dest_id are |
| // identical. |
| auto configuration_request_acl_packet = l2cap::testing::AclConfigReq( |
| kCommandId, kConnectionHandle, src_id, params); |
| const auto& configuration_request_header = |
| configuration_request_acl_packet.To<hci_spec::ACLDataHeader>(); |
| auto configuration_request_header_len = sizeof(configuration_request_header); |
| auto configuration_request_payload_len = |
| le16toh(configuration_request_header.data_total_length); |
| auto configuration_request_packet = |
| DynamicByteBuffer(configuration_request_payload_len); |
| configuration_request_acl_packet.Copy(&configuration_request_packet, |
| configuration_request_header_len, |
| configuration_request_payload_len); |
| fake_l2cap.HandlePdu(kConnectionHandle, configuration_request_packet); |
| |
| // Anticipate that we then receive a ConfigurationResponse after we send a |
| // Manually create the expected ConfigurationRequest with no payload. |
| StaticByteBuffer expected_response( |
| // Configuration request command code, CommandId associated with the test |
| l2cap::kConfigurationResponse, |
| kCommandId, |
| // Payload length (6 total bytes) |
| 0x06, |
| 0x00, |
| // Source ID (2 bytes) |
| LowerBits(src_id), |
| UpperBits(src_id), |
| // No continuation flags (2 bytes) |
| 0x00, |
| 0x00, |
| // Result (Success) |
| LowerBits(0x0000), |
| UpperBits(0x0000)); |
| EXPECT_TRUE(ContainersEqual(expected_response, *received_packet)); |
| EXPECT_TRUE(fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| ->configuration_request_received()); |
| EXPECT_TRUE(fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| ->configuration_response_received()); |
| EXPECT_TRUE(fake_l2cap.FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| ->opened()); |
| |
| // Try to open up the same channel again. |
| auto second_connection_acl_packet = l2cap::testing::AclConnectionReq( |
| kCommandId, kConnectionHandle, src_id, kPsm); |
| const auto& second_connection_header = |
| second_connection_acl_packet.To<hci_spec::ACLDataHeader>(); |
| auto second_connection_header_len = sizeof(second_connection_header); |
| auto second_connection_payload_len = |
| le16toh(second_connection_header.data_total_length); |
| auto second_connection_packet = |
| DynamicByteBuffer(second_connection_payload_len); |
| second_connection_acl_packet.Copy(&second_connection_packet, |
| second_connection_header_len, |
| second_connection_payload_len); |
| fake_l2cap.HandlePdu(kConnectionHandle, second_connection_packet); |
| |
| // Anticipate that we will receive a rejection as the remote ID has already |
| // been registered. |
| auto second_expected_acl_response = l2cap::testing::AclConnectionRsp( |
| kCommandId, |
| kConnectionHandle, |
| src_id, |
| l2cap::kInvalidChannelId, |
| l2cap::ConnectionResult::kSourceCIDAlreadyAllocated); |
| auto second_expected_response = second_expected_acl_response.view( |
| sizeof(hci_spec::ACLDataHeader) + sizeof(l2cap::CommandHeader)); |
| EXPECT_TRUE(ContainersEqual(second_expected_response, *received_packet)); |
| } |
| |
| TEST(FakeDynamicChannelTest, FailWhenOutOfIds) { |
| auto unexpected_cb = [](auto handle, auto& pdu) {}; |
| std::unique_ptr<ByteBuffer> received_packet; |
| auto send_cb = [&received_packet]( |
| auto kConnectionHandle, auto cid, auto& buffer) { |
| received_packet = std::make_unique<DynamicByteBuffer>(buffer); |
| }; |
| auto fewer_ids_fake_l2cap_ = |
| FakeL2cap(send_cb, unexpected_cb, l2cap::kFirstDynamicChannelId); |
| auto server = std::make_unique<FakeSignalingServer>(); |
| server->RegisterWithL2cap(&fewer_ids_fake_l2cap_); |
| auto channel_cb = [](auto fake_dynamic_channel) {}; |
| fewer_ids_fake_l2cap_.RegisterService(kPsm, channel_cb); |
| l2cap::ChannelId src_id = l2cap::kFirstDynamicChannelId; |
| |
| // Assemble and send the ConnectionRequest to connect, but not open, the |
| // channel. |
| auto connection_acl_packet = l2cap::testing::AclConnectionReq( |
| kCommandId, kConnectionHandle, src_id, kPsm); |
| const auto& connection_header = |
| connection_acl_packet.To<hci_spec::ACLDataHeader>(); |
| auto connection_header_len = sizeof(connection_header); |
| auto connection_payload_len = le16toh(connection_header.data_total_length); |
| auto connection_packet = DynamicByteBuffer(connection_payload_len); |
| connection_acl_packet.Copy( |
| &connection_packet, connection_header_len, connection_payload_len); |
| fewer_ids_fake_l2cap_.HandlePdu(kConnectionHandle, connection_packet); |
| EXPECT_FALSE(fewer_ids_fake_l2cap_ |
| .FindDynamicChannelByLocalId(kConnectionHandle, src_id) |
| ->opened()); |
| |
| // The FakeL2cap instance should now be out of ChannelIds to assign. |
| l2cap::ChannelId second_src_id = l2cap::kFirstDynamicChannelId + 1; |
| auto second_connection_acl_packet = l2cap::testing::AclConnectionReq( |
| kCommandId, kConnectionHandle, second_src_id, kPsm); |
| const auto& second_connection_header = |
| second_connection_acl_packet.To<hci_spec::ACLDataHeader>(); |
| auto second_connection_header_len = sizeof(second_connection_header); |
| auto second_connection_payload_len = |
| le16toh(second_connection_header.data_total_length); |
| auto second_connection_packet = |
| DynamicByteBuffer(second_connection_payload_len); |
| second_connection_acl_packet.Copy(&second_connection_packet, |
| second_connection_header_len, |
| second_connection_payload_len); |
| fewer_ids_fake_l2cap_.HandlePdu(kConnectionHandle, second_connection_packet); |
| |
| // Anticipate that we will receive a rejection as there are no Ids left. |
| auto expected_acl_response = |
| l2cap::testing::AclConnectionRsp(kCommandId, |
| kConnectionHandle, |
| second_src_id, |
| l2cap::kInvalidChannelId, |
| l2cap::ConnectionResult::kNoResources); |
| auto expected_response = expected_acl_response.view( |
| sizeof(hci_spec::ACLDataHeader) + sizeof(l2cap::CommandHeader)); |
| EXPECT_TRUE(ContainersEqual(expected_response, *received_packet)); |
| EXPECT_FALSE( |
| fewer_ids_fake_l2cap_ |
| .FindDynamicChannelByLocalId(kConnectionHandle, second_src_id) |
| .is_alive()); |
| } |
| |
| } // namespace |
| } // namespace bt::testing |