blob: da16c21def627c71c19995e8a5b1e3848a870144 [file] [log] [blame]
// 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 "sco_connection_manager.h"
#include "src/connectivity/bluetooth/core/bt-host/testing/controller_test.h"
#include "src/connectivity/bluetooth/core/bt-host/testing/mock_controller.h"
#include "src/connectivity/bluetooth/core/bt-host/testing/test_packets.h"
namespace bt::sco {
namespace {
using ConnectionResult = ScoConnectionManager::ConnectionResult;
constexpr hci::ConnectionHandle kAclConnectionHandle = 0x40;
constexpr hci::ConnectionHandle kScoConnectionHandle = 0x41;
const DeviceAddress kLocalAddress(DeviceAddress::Type::kBREDR,
{0x00, 0x00, 0x00, 0x00, 0x00, 0x01});
const DeviceAddress kPeerAddress(DeviceAddress::Type::kBREDR, {0x00, 0x00, 0x00, 0x00, 0x00, 0x02});
constexpr hci::SynchronousConnectionParameters kConnectionParams{
.transmit_bandwidth = 1,
.receive_bandwidth = 2,
.transmit_coding_format =
hci::VendorCodingFormat{
.coding_format = hci::CodingFormat::kMSbc,
.company_id = 3,
.vendor_codec_id = 4,
},
.receive_coding_format =
hci::VendorCodingFormat{
.coding_format = hci::CodingFormat::kCvsd,
.company_id = 5,
.vendor_codec_id = 6,
},
.transmit_codec_frame_size_bytes = 7,
.receive_codec_frame_size_bytes = 8,
.input_bandwidth = 9,
.output_bandwidth = 10,
.input_coding_format =
hci::VendorCodingFormat{
.coding_format = hci::CodingFormat::kALaw,
.company_id = 11,
.vendor_codec_id = 12,
},
.output_coding_format =
hci::VendorCodingFormat{
.coding_format = hci::CodingFormat::kLinearPcm,
.company_id = 13,
.vendor_codec_id = 14,
},
.input_coded_data_size_bits = 15,
.output_coded_data_size_bits = 16,
.input_pcm_data_format = hci::PcmDataFormat::k1sComplement,
.output_pcm_data_format = hci::PcmDataFormat::k2sComplement,
.input_pcm_sample_payload_msb_position = 17,
.output_pcm_sample_payload_msb_position = 18,
.input_data_path = hci::ScoDataPath::kAudioTestMode,
.output_data_path = hci::ScoDataPath::kHci,
.input_transport_unit_size_bits = 19,
.output_transport_unit_size_bits = 20,
.max_latency_ms = 21,
.packet_types = 257,
.retransmission_effort = hci::ScoRetransmissionEffort::kQualityOptimized,
};
using TestingBase = bt::testing::ControllerTest<bt::testing::MockController>;
class ScoConnectionManagerTest : public TestingBase {
public:
ScoConnectionManagerTest() = default;
~ScoConnectionManagerTest() override = default;
void SetUp() override {
TestingBase::SetUp();
InitializeACLDataChannel();
StartTestDevice();
manager_ = std::make_unique<ScoConnectionManager>(PeerId(1), kAclConnectionHandle, kPeerAddress,
kLocalAddress, transport()->WeakPtr());
}
void TearDown() override {
manager_.reset();
RunLoopUntilIdle();
TestingBase::TearDown();
}
void DestroyManager() { manager_.reset(); }
ScoConnectionManager* manager() const { return manager_.get(); }
private:
std::unique_ptr<ScoConnectionManager> manager_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ScoConnectionManagerTest);
};
using SCO_ScoConnectionManagerTest = ScoConnectionManagerTest;
TEST_F(SCO_ScoConnectionManagerTest, OpenConnectionSuccess) {
auto setup_status_packet = testing::CommandStatusPacket(hci::kEnhancedSetupSynchronousConnection,
hci::StatusCode::kSuccess);
auto conn_complete_packet = testing::SynchronousConnectionCompletePacket(
kScoConnectionHandle, kPeerAddress, hci::LinkType::kExtendedSCO, hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedSetupSynchronousConnectionPacket(kAclConnectionHandle, kConnectionParams),
&setup_status_packet, &conn_complete_packet);
ConnectionResult conn_result;
auto conn_cb = [&conn_result](auto result) { conn_result = std::move(result); };
auto req_handle = manager()->OpenConnection(kConnectionParams, std::move(conn_cb));
RunLoopUntilIdle();
ASSERT_TRUE(conn_result.is_ok());
ASSERT_TRUE(conn_result.value());
EXPECT_EQ(conn_result.value()->handle(), kScoConnectionHandle);
EXPECT_CMD_PACKET_OUT(test_device(), testing::DisconnectPacket(kScoConnectionHandle));
conn_result.value()->Close();
RunLoopUntilIdle();
}
TEST_F(SCO_ScoConnectionManagerTest, OpenConnectionAndReceiveFailureStatusEvent) {
auto setup_status_packet = testing::CommandStatusPacket(
hci::kEnhancedSetupSynchronousConnection, hci::StatusCode::kConnectionLimitExceeded);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedSetupSynchronousConnectionPacket(kAclConnectionHandle, kConnectionParams),
&setup_status_packet);
ConnectionResult conn;
auto conn_cb = [&conn](auto cb_result) { conn = std::move(cb_result); };
auto req_handle = manager()->OpenConnection(kConnectionParams, std::move(conn_cb));
RunLoopUntilIdle();
ASSERT_TRUE(conn.is_error());
EXPECT_EQ(conn.error(), HostError::kFailed);
}
TEST_F(SCO_ScoConnectionManagerTest, OpenConnectionAndReceiveFailureCompleteEvent) {
auto setup_status_packet = testing::CommandStatusPacket(hci::kEnhancedSetupSynchronousConnection,
hci::StatusCode::kSuccess);
auto conn_complete_packet = testing::SynchronousConnectionCompletePacket(
kScoConnectionHandle, kPeerAddress, hci::LinkType::kExtendedSCO,
hci::StatusCode::kConnectionFailedToBeEstablished);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedSetupSynchronousConnectionPacket(kAclConnectionHandle, kConnectionParams),
&setup_status_packet, &conn_complete_packet);
ConnectionResult conn;
auto conn_cb = [&conn](auto cb_result) { conn = std::move(cb_result); };
auto req_handle = manager()->OpenConnection(kConnectionParams, std::move(conn_cb));
RunLoopUntilIdle();
ASSERT_TRUE(conn.is_error());
EXPECT_EQ(conn.error(), HostError::kFailed);
}
TEST_F(SCO_ScoConnectionManagerTest, IgnoreWrongAddressInConnectionComplete) {
auto setup_status_packet = testing::CommandStatusPacket(hci::kEnhancedSetupSynchronousConnection,
hci::StatusCode::kSuccess);
const DeviceAddress kWrongPeerAddress(DeviceAddress::Type::kBREDR,
{0x00, 0x00, 0x00, 0x00, 0x00, 0x05});
auto conn_complete_packet_wrong = testing::SynchronousConnectionCompletePacket(
kScoConnectionHandle, kWrongPeerAddress, hci::LinkType::kExtendedSCO,
hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedSetupSynchronousConnectionPacket(kAclConnectionHandle, kConnectionParams),
&setup_status_packet, &conn_complete_packet_wrong);
ConnectionResult conn_result;
auto conn_cb = [&conn_result](auto result) { conn_result = std::move(result); };
auto req_handle = manager()->OpenConnection(kConnectionParams, std::move(conn_cb));
RunLoopUntilIdle();
EXPECT_TRUE(conn_result.is_pending());
// Ensure subsequent correct complete packet completes request.
auto conn_complete_packet = testing::SynchronousConnectionCompletePacket(
kScoConnectionHandle, kPeerAddress, hci::LinkType::kExtendedSCO, hci::StatusCode::kSuccess);
test_device()->SendCommandChannelPacket(conn_complete_packet);
RunLoopUntilIdle();
ASSERT_TRUE(conn_result.is_ok());
EXPECT_CMD_PACKET_OUT(test_device(), testing::DisconnectPacket(kScoConnectionHandle));
}
TEST_F(SCO_ScoConnectionManagerTest, UnexpectedConnectionCompleteDisconnectsConnection) {
auto conn_complete_packet = testing::SynchronousConnectionCompletePacket(
kScoConnectionHandle, kPeerAddress, hci::LinkType::kExtendedSCO, hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(test_device(), testing::DisconnectPacket(kScoConnectionHandle));
test_device()->SendCommandChannelPacket(conn_complete_packet);
RunLoopUntilIdle();
}
TEST_F(SCO_ScoConnectionManagerTest, DestroyingManagerClosesConnections) {
auto setup_status_packet = testing::CommandStatusPacket(hci::kEnhancedSetupSynchronousConnection,
hci::StatusCode::kSuccess);
auto conn_complete_packet = testing::SynchronousConnectionCompletePacket(
kScoConnectionHandle, kPeerAddress, hci::LinkType::kExtendedSCO, hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedSetupSynchronousConnectionPacket(kAclConnectionHandle, kConnectionParams),
&setup_status_packet, &conn_complete_packet);
ConnectionResult conn_result;
auto conn_cb = [&conn_result](ConnectionResult result) { conn_result = std::move(result); };
auto req_handle = manager()->OpenConnection(kConnectionParams, std::move(conn_cb));
RunLoopUntilIdle();
EXPECT_TRUE(conn_result.is_ok());
EXPECT_CMD_PACKET_OUT(test_device(), testing::DisconnectPacket(kScoConnectionHandle));
DestroyManager();
RunLoopUntilIdle();
// Ref should still be valid.
EXPECT_TRUE(conn_result.value());
}
TEST_F(SCO_ScoConnectionManagerTest, QueueThreeRequestsCancelsSecond) {
const hci::ConnectionHandle handle_0 = kScoConnectionHandle;
const hci::ConnectionHandle handle_1 = handle_0 + 1;
const hci::ConnectionHandle handle_2 = handle_1 + 1;
auto setup_status_packet = testing::CommandStatusPacket(hci::kEnhancedSetupSynchronousConnection,
hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedSetupSynchronousConnectionPacket(kAclConnectionHandle, kConnectionParams),
&setup_status_packet);
ConnectionResult conn_0;
auto conn_cb_0 = [&conn_0](auto cb_conn) { conn_0 = std::move(cb_conn); };
auto req_handle_0 = manager()->OpenConnection(kConnectionParams, std::move(conn_cb_0));
ConnectionResult conn_result_1;
auto conn_cb_1 = [&conn_result_1](auto cb_result) { conn_result_1 = std::move(cb_result); };
auto req_handle_1 = manager()->OpenConnection(kConnectionParams, std::move(conn_cb_1));
ConnectionResult conn_2;
auto conn_cb_2 = [&conn_2](auto cb_conn) { conn_2 = std::move(cb_conn); };
auto req_handle_2 = manager()->OpenConnection(kConnectionParams, std::move(conn_cb_2));
RunLoopUntilIdle();
EXPECT_TRUE(conn_0.is_pending());
EXPECT_TRUE(conn_2.is_pending());
ASSERT_TRUE(conn_result_1.is_error());
EXPECT_EQ(conn_result_1.error(), HostError::kCanceled);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedSetupSynchronousConnectionPacket(kAclConnectionHandle, kConnectionParams),
&setup_status_packet);
auto conn_complete_packet_0 = testing::SynchronousConnectionCompletePacket(
handle_0, kPeerAddress, hci::LinkType::kExtendedSCO, hci::StatusCode::kSuccess);
test_device()->SendCommandChannelPacket(conn_complete_packet_0);
RunLoopUntilIdle();
ASSERT_TRUE(conn_0.is_ok());
EXPECT_TRUE(conn_2.is_pending());
auto conn_complete_packet_2 = testing::SynchronousConnectionCompletePacket(
handle_2, kPeerAddress, hci::LinkType::kExtendedSCO, hci::StatusCode::kSuccess);
test_device()->SendCommandChannelPacket(conn_complete_packet_2);
RunLoopUntilIdle();
ASSERT_TRUE(conn_2.is_ok());
// Send status and complete events so second disconnect command isn't queued in CommandChannel.
auto disconn_status_packet_0 =
testing::CommandStatusPacket(hci::kDisconnect, hci::StatusCode::kSuccess);
auto disconn_complete_0 = testing::DisconnectionCompletePacket(handle_0);
EXPECT_CMD_PACKET_OUT(test_device(), testing::DisconnectPacket(handle_0),
&disconn_status_packet_0, &disconn_complete_0);
conn_0.value()->Deactivate();
EXPECT_CMD_PACKET_OUT(test_device(), testing::DisconnectPacket(handle_2));
conn_2.value()->Deactivate();
RunLoopUntilIdle();
}
TEST_F(SCO_ScoConnectionManagerTest, HandleReuse) {
auto setup_status_packet = testing::CommandStatusPacket(hci::kEnhancedSetupSynchronousConnection,
hci::StatusCode::kSuccess);
auto conn_complete_packet = testing::SynchronousConnectionCompletePacket(
kScoConnectionHandle, kPeerAddress, hci::LinkType::kExtendedSCO, hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedSetupSynchronousConnectionPacket(kAclConnectionHandle, kConnectionParams),
&setup_status_packet, &conn_complete_packet);
ConnectionResult conn_result;
auto conn_cb = [&conn_result](auto cb_conn) { conn_result = std::move(cb_conn); };
auto req_handle_0 = manager()->OpenConnection(kConnectionParams, conn_cb);
RunLoopUntilIdle();
ASSERT_TRUE(conn_result.is_ok());
auto conn = conn_result.take_value();
EXPECT_EQ(conn->handle(), kScoConnectionHandle);
auto disconn_status_packet =
testing::CommandStatusPacket(hci::kDisconnect, hci::StatusCode::kSuccess);
auto disconn_complete = testing::DisconnectionCompletePacket(kScoConnectionHandle);
EXPECT_CMD_PACKET_OUT(test_device(), testing::DisconnectPacket(kScoConnectionHandle),
&disconn_status_packet, &disconn_complete);
conn->Deactivate();
conn_result = fit::pending();
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedSetupSynchronousConnectionPacket(kAclConnectionHandle, kConnectionParams),
&setup_status_packet, &conn_complete_packet);
auto req_handle_1 = manager()->OpenConnection(kConnectionParams, conn_cb);
RunLoopUntilIdle();
ASSERT_TRUE(conn_result.is_ok());
EXPECT_EQ(conn_result.value()->handle(), kScoConnectionHandle);
EXPECT_CMD_PACKET_OUT(test_device(), testing::DisconnectPacket(kScoConnectionHandle));
}
TEST_F(SCO_ScoConnectionManagerTest, AcceptConnectionSuccess) {
ConnectionResult conn;
auto conn_cb = [&conn](auto cb_conn) {
EXPECT_TRUE(cb_conn.is_ok());
conn = std::move(cb_conn);
};
auto req_handle = manager()->AcceptConnection(kConnectionParams, std::move(conn_cb));
auto conn_req_packet = testing::ConnectionRequestPacket(kPeerAddress, hci::LinkType::kSCO);
test_device()->SendCommandChannelPacket(conn_req_packet);
auto accept_status_packet = testing::CommandStatusPacket(
hci::kEnhancedAcceptSynchronousConnectionRequest, hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedAcceptSynchronousConnectionRequestPacket(kPeerAddress, kConnectionParams),
&accept_status_packet);
RunLoopUntilIdle();
EXPECT_TRUE(conn.is_pending());
test_device()->SendCommandChannelPacket(testing::SynchronousConnectionCompletePacket(
kScoConnectionHandle, kPeerAddress, hci::LinkType::kSCO, hci::StatusCode::kSuccess));
RunLoopUntilIdle();
ASSERT_TRUE(conn.is_ok());
EXPECT_EQ(conn.value()->handle(), kScoConnectionHandle);
EXPECT_CMD_PACKET_OUT(test_device(), testing::DisconnectPacket(kScoConnectionHandle));
}
TEST_F(SCO_ScoConnectionManagerTest, AcceptConnectionStatusFailure) {
ConnectionResult conn;
auto conn_cb = [&conn](auto cb_conn) { conn = std::move(cb_conn); };
auto req_handle = manager()->AcceptConnection(kConnectionParams, std::move(conn_cb));
EXPECT_TRUE(conn.is_pending());
auto conn_req_packet = testing::ConnectionRequestPacket(kPeerAddress, hci::LinkType::kSCO);
test_device()->SendCommandChannelPacket(conn_req_packet);
auto accept_status_packet = testing::CommandStatusPacket(
hci::kEnhancedAcceptSynchronousConnectionRequest, hci::StatusCode::kConnectionLimitExceeded);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedAcceptSynchronousConnectionRequestPacket(kPeerAddress, kConnectionParams),
&accept_status_packet);
RunLoopUntilIdle();
ASSERT_TRUE(conn.is_error());
EXPECT_EQ(conn.error(), HostError::kFailed);
}
TEST_F(SCO_ScoConnectionManagerTest, AcceptConnectionAndReceiveCompleteEventWithFailureStatus) {
ConnectionResult conn;
auto conn_cb = [&conn](auto cb_conn) { conn = std::move(cb_conn); };
auto req_handle = manager()->AcceptConnection(kConnectionParams, std::move(conn_cb));
auto conn_req_packet = testing::ConnectionRequestPacket(kPeerAddress, hci::LinkType::kSCO);
test_device()->SendCommandChannelPacket(conn_req_packet);
auto accept_status_packet = testing::CommandStatusPacket(
hci::kEnhancedAcceptSynchronousConnectionRequest, hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedAcceptSynchronousConnectionRequestPacket(kPeerAddress, kConnectionParams),
&accept_status_packet);
RunLoopUntilIdle();
EXPECT_TRUE(conn.is_pending());
test_device()->SendCommandChannelPacket(testing::SynchronousConnectionCompletePacket(
kScoConnectionHandle, kPeerAddress, hci::LinkType::kSCO,
hci::StatusCode::kConnectionFailedToBeEstablished));
RunLoopUntilIdle();
ASSERT_TRUE(conn.is_error());
EXPECT_EQ(conn.error(), HostError::kFailed);
}
TEST_F(SCO_ScoConnectionManagerTest, RejectInboundRequestWhileInitiatorRequestPending) {
size_t conn_cb_count = 0;
auto conn_cb = [&conn_cb_count](auto cb_conn) { conn_cb_count++; };
auto setup_status_packet = testing::CommandStatusPacket(hci::kEnhancedSetupSynchronousConnection,
hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedSetupSynchronousConnectionPacket(kAclConnectionHandle, kConnectionParams),
&setup_status_packet);
auto req_handle = manager()->OpenConnection(kConnectionParams, std::move(conn_cb));
auto conn_req_packet = testing::ConnectionRequestPacket(kPeerAddress, hci::LinkType::kSCO);
test_device()->SendCommandChannelPacket(conn_req_packet);
auto reject_status_packet = testing::CommandStatusPacket(hci::kRejectSynchronousConnectionRequest,
hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(test_device(),
testing::RejectSynchronousConnectionRequest(
kPeerAddress, hci::StatusCode::kConnectionRejectedBadBdAddr),
&reject_status_packet);
RunLoopUntilIdle();
EXPECT_EQ(conn_cb_count, 0u);
// Destroy manager so that callback gets called before callback reference is invalid.
DestroyManager();
EXPECT_EQ(conn_cb_count, 1u);
}
TEST_F(SCO_ScoConnectionManagerTest, RejectInboundRequestWhenNoRequestsPending) {
auto conn_req_packet = testing::ConnectionRequestPacket(kPeerAddress, hci::LinkType::kSCO);
test_device()->SendCommandChannelPacket(conn_req_packet);
auto reject_status_packet = testing::CommandStatusPacket(hci::kRejectSynchronousConnectionRequest,
hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(test_device(),
testing::RejectSynchronousConnectionRequest(
kPeerAddress, hci::StatusCode::kConnectionRejectedBadBdAddr),
&reject_status_packet);
RunLoopUntilIdle();
}
TEST_F(SCO_ScoConnectionManagerTest, IgnoreInboundAclRequest) {
auto conn_req_packet = testing::ConnectionRequestPacket(kPeerAddress, hci::LinkType::kACL);
test_device()->SendCommandChannelPacket(conn_req_packet);
RunLoopUntilIdle();
}
TEST_F(SCO_ScoConnectionManagerTest, IgnoreInboundRequestWrongPeerAddress) {
const DeviceAddress address(DeviceAddress::Type::kBREDR, {0x00, 0x00, 0x00, 0x00, 0x00, 0x05});
auto conn_req_packet = testing::ConnectionRequestPacket(address, hci::LinkType::kACL);
test_device()->SendCommandChannelPacket(conn_req_packet);
RunLoopUntilIdle();
}
TEST_F(SCO_ScoConnectionManagerTest, QueueTwoAcceptConnectionRequestsCancelsFirstRequest) {
ConnectionResult conn_0;
auto conn_cb = [&conn_0](auto cb_conn) { conn_0 = std::move(cb_conn); };
auto req_handle_0 = manager()->AcceptConnection(kConnectionParams, conn_cb);
auto second_conn_params = kConnectionParams;
second_conn_params.transmit_bandwidth = 99;
ConnectionResult conn_1;
auto req_handle_1 = manager()->AcceptConnection(
second_conn_params, [&conn_1](auto cb_conn) { conn_1 = std::move(cb_conn); });
ASSERT_TRUE(conn_0.is_error());
EXPECT_EQ(conn_0.error(), HostError::kCanceled);
auto conn_req_packet = testing::ConnectionRequestPacket(kPeerAddress, hci::LinkType::kSCO);
test_device()->SendCommandChannelPacket(conn_req_packet);
auto accept_status_packet = testing::CommandStatusPacket(
hci::kEnhancedAcceptSynchronousConnectionRequest, hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedAcceptSynchronousConnectionRequestPacket(kPeerAddress, second_conn_params),
&accept_status_packet);
RunLoopUntilIdle();
EXPECT_TRUE(conn_1.is_pending());
test_device()->SendCommandChannelPacket(testing::SynchronousConnectionCompletePacket(
kScoConnectionHandle, kPeerAddress, hci::LinkType::kSCO, hci::StatusCode::kSuccess));
RunLoopUntilIdle();
ASSERT_TRUE(conn_1.is_ok());
EXPECT_EQ(conn_1.value()->handle(), kScoConnectionHandle);
EXPECT_CMD_PACKET_OUT(test_device(), testing::DisconnectPacket(kScoConnectionHandle));
}
TEST_F(SCO_ScoConnectionManagerTest, QueueSecondAcceptRequestAfterFirstRequestReceivesEvent) {
ConnectionResult conn_0;
auto req_handle_0 = manager()->AcceptConnection(
kConnectionParams, [&conn_0](auto cb_conn) { conn_0 = std::move(cb_conn); });
auto conn_req_packet = testing::ConnectionRequestPacket(kPeerAddress, hci::LinkType::kSCO);
test_device()->SendCommandChannelPacket(conn_req_packet);
EXPECT_CMD_PACKET_OUT(test_device(), testing::EnhancedAcceptSynchronousConnectionRequestPacket(
kPeerAddress, kConnectionParams));
RunLoopUntilIdle();
ConnectionResult conn_1;
auto req_handle_1 = manager()->AcceptConnection(
kConnectionParams, [&conn_1](auto cb_conn) { conn_1 = std::move(cb_conn); });
// First request should not be cancelled because a request event was received.
EXPECT_TRUE(conn_0.is_pending());
// Send failure status to fail first request.
test_device()->SendCommandChannelPacket(testing::CommandStatusPacket(
hci::kEnhancedAcceptSynchronousConnectionRequest, hci::StatusCode::kCommandDisallowed));
RunLoopUntilIdle();
ASSERT_TRUE(conn_0.is_error());
EXPECT_EQ(conn_0.error(), HostError::kFailed);
// Second request should now be in progress.
test_device()->SendCommandChannelPacket(conn_req_packet);
auto accept_status_packet = testing::CommandStatusPacket(
hci::kEnhancedAcceptSynchronousConnectionRequest, hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedAcceptSynchronousConnectionRequestPacket(kPeerAddress, kConnectionParams),
&accept_status_packet);
RunLoopUntilIdle();
EXPECT_TRUE(conn_1.is_pending());
test_device()->SendCommandChannelPacket(testing::SynchronousConnectionCompletePacket(
kScoConnectionHandle, kPeerAddress, hci::LinkType::kSCO, hci::StatusCode::kSuccess));
RunLoopUntilIdle();
ASSERT_TRUE(conn_1.is_ok());
EXPECT_EQ(conn_1.value()->handle(), kScoConnectionHandle);
EXPECT_CMD_PACKET_OUT(test_device(), testing::DisconnectPacket(kScoConnectionHandle));
}
TEST_F(SCO_ScoConnectionManagerTest, RequestsCancelledOnManagerDestruction) {
auto setup_status_packet = testing::CommandStatusPacket(hci::kEnhancedSetupSynchronousConnection,
hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedSetupSynchronousConnectionPacket(kAclConnectionHandle, kConnectionParams),
&setup_status_packet);
ConnectionResult conn_0;
auto conn_cb_0 = [&conn_0](auto cb_conn) { conn_0 = std::move(cb_conn); };
auto req_handle_0 = manager()->OpenConnection(kConnectionParams, std::move(conn_cb_0));
ConnectionResult conn_1;
auto conn_cb_1 = [&conn_1](auto cb_conn) { conn_1 = std::move(cb_conn); };
auto req_handle_1 = manager()->OpenConnection(kConnectionParams, std::move(conn_cb_1));
RunLoopUntilIdle();
DestroyManager();
ASSERT_TRUE(conn_0.is_error());
EXPECT_EQ(conn_0.error(), HostError::kCanceled);
ASSERT_TRUE(conn_1.is_error());
EXPECT_EQ(conn_1.error(), HostError::kCanceled);
}
TEST_F(SCO_ScoConnectionManagerTest, AcceptConnectionExplicitlyCancelledByClient) {
ConnectionResult conn;
auto conn_cb = [&conn](auto cb_conn) { conn = std::move(cb_conn); };
auto req_handle = manager()->AcceptConnection(kConnectionParams, std::move(conn_cb));
req_handle.Cancel();
ASSERT_TRUE(conn.is_error());
EXPECT_EQ(conn.error(), HostError::kCanceled);
auto conn_req_packet = testing::ConnectionRequestPacket(kPeerAddress, hci::LinkType::kSCO);
test_device()->SendCommandChannelPacket(conn_req_packet);
auto reject_status_packet = testing::CommandStatusPacket(hci::kRejectSynchronousConnectionRequest,
hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(test_device(),
testing::RejectSynchronousConnectionRequest(
kPeerAddress, hci::StatusCode::kConnectionRejectedBadBdAddr),
&reject_status_packet);
RunLoopUntilIdle();
ASSERT_TRUE(conn.is_error());
EXPECT_EQ(conn.error(), HostError::kCanceled);
}
TEST_F(SCO_ScoConnectionManagerTest, AcceptConnectionCancelledByClientDestroyingHandle) {
ConnectionResult conn;
auto conn_cb = [&conn](auto cb_conn) { conn = std::move(cb_conn); };
// req_handle destroyed at end of scope
{ auto req_handle = manager()->AcceptConnection(kConnectionParams, std::move(conn_cb)); }
ASSERT_TRUE(conn.is_error());
EXPECT_EQ(conn.error(), HostError::kCanceled);
auto conn_req_packet = testing::ConnectionRequestPacket(kPeerAddress, hci::LinkType::kSCO);
test_device()->SendCommandChannelPacket(conn_req_packet);
auto reject_status_packet = testing::CommandStatusPacket(hci::kRejectSynchronousConnectionRequest,
hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(test_device(),
testing::RejectSynchronousConnectionRequest(
kPeerAddress, hci::StatusCode::kConnectionRejectedBadBdAddr),
&reject_status_packet);
RunLoopUntilIdle();
ASSERT_TRUE(conn.is_error());
EXPECT_EQ(conn.error(), HostError::kCanceled);
}
TEST_F(SCO_ScoConnectionManagerTest, OpenConnectionCantBeCancelledOnceInProgress) {
auto setup_status_packet = testing::CommandStatusPacket(hci::kEnhancedSetupSynchronousConnection,
hci::StatusCode::kSuccess);
auto conn_complete_packet = testing::SynchronousConnectionCompletePacket(
kScoConnectionHandle, kPeerAddress, hci::LinkType::kExtendedSCO, hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedSetupSynchronousConnectionPacket(kAclConnectionHandle, kConnectionParams),
&setup_status_packet);
ConnectionResult conn;
auto conn_cb = [&conn](auto cb_conn) { conn = std::move(cb_conn); };
auto req_handle = manager()->OpenConnection(kConnectionParams, std::move(conn_cb));
req_handle.Cancel();
RunLoopUntilIdle();
EXPECT_TRUE(conn.is_pending());
test_device()->SendCommandChannelPacket(conn_complete_packet);
RunLoopUntilIdle();
ASSERT_TRUE(conn.is_ok());
EXPECT_EQ(conn.value()->handle(), kScoConnectionHandle);
EXPECT_CMD_PACKET_OUT(test_device(), testing::DisconnectPacket(kScoConnectionHandle));
conn.value()->Close();
RunLoopUntilIdle();
}
TEST_F(SCO_ScoConnectionManagerTest, QueueTwoRequestsAndCancelSecond) {
const hci::ConnectionHandle handle_0 = kScoConnectionHandle;
auto setup_status_packet = testing::CommandStatusPacket(hci::kEnhancedSetupSynchronousConnection,
hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(
test_device(),
testing::EnhancedSetupSynchronousConnectionPacket(kAclConnectionHandle, kConnectionParams),
&setup_status_packet);
ConnectionResult conn_0;
auto conn_cb_0 = [&conn_0](auto cb_conn) { conn_0 = std::move(cb_conn); };
auto req_handle_0 = manager()->OpenConnection(kConnectionParams, std::move(conn_cb_0));
size_t cb_count_1 = 0;
ConnectionResult conn_1;
auto conn_cb_1 = [&cb_count_1, &conn_1](auto cb_conn) {
cb_count_1++;
conn_1 = std::move(cb_conn);
};
auto req_handle_1 = manager()->OpenConnection(kConnectionParams, std::move(conn_cb_1));
RunLoopUntilIdle();
EXPECT_TRUE(conn_0.is_pending());
req_handle_1.Cancel();
EXPECT_EQ(cb_count_1, 1u);
ASSERT_TRUE(conn_1.is_error());
EXPECT_EQ(conn_1.error(), HostError::kCanceled);
auto conn_complete_packet_0 = testing::SynchronousConnectionCompletePacket(
handle_0, kPeerAddress, hci::LinkType::kExtendedSCO, hci::StatusCode::kSuccess);
test_device()->SendCommandChannelPacket(conn_complete_packet_0);
RunLoopUntilIdle();
ASSERT_TRUE(conn_0.is_ok());
EXPECT_EQ(cb_count_1, 1u);
auto disconn_status_packet_0 =
testing::CommandStatusPacket(hci::kDisconnect, hci::StatusCode::kSuccess);
auto disconn_complete_0 = testing::DisconnectionCompletePacket(handle_0);
EXPECT_CMD_PACKET_OUT(test_device(), testing::DisconnectPacket(handle_0),
&disconn_status_packet_0, &disconn_complete_0);
conn_0.value()->Deactivate();
RunLoopUntilIdle();
}
} // namespace
} // namespace bt::sco