| // Copyright 2018 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 "garnet/drivers/bluetooth/lib/gap/bredr_connection_manager.h" |
| |
| #include "garnet/drivers/bluetooth/lib/common/test_helpers.h" |
| #include "garnet/drivers/bluetooth/lib/data/fake_domain.h" |
| #include "garnet/drivers/bluetooth/lib/gap/remote_device_cache.h" |
| #include "garnet/drivers/bluetooth/lib/hci/hci.h" |
| #include "garnet/drivers/bluetooth/lib/testing/fake_controller_test.h" |
| #include "garnet/drivers/bluetooth/lib/testing/test_controller.h" |
| |
| namespace btlib { |
| namespace gap { |
| namespace { |
| |
| using ::btlib::testing::CommandTransaction; |
| |
| using common::DeviceAddress; |
| using common::LowerBits; |
| using common::UpperBits; |
| |
| using TestingBase = |
| ::btlib::testing::FakeControllerTest<::btlib::testing::TestController>; |
| |
| constexpr hci::ConnectionHandle kConnectionHandle = 0x0BAA; |
| const DeviceAddress kTestDevAddr(DeviceAddress::Type::kBREDR, |
| "00:00:00:00:00:01"); |
| |
| #define TEST_DEV_ADDR_BYTES_LE 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 |
| |
| // clang-format off |
| |
| const auto kReadScanEnable = common::CreateStaticByteBuffer( |
| LowerBits(hci::kReadScanEnable), UpperBits(hci::kReadScanEnable), |
| 0x00 // No parameters |
| ); |
| |
| #define READ_SCAN_ENABLE_RSP(scan_enable) \ |
| common::CreateStaticByteBuffer(hci::kCommandCompleteEventCode, 0x05, 0xF0, \ |
| LowerBits(hci::kReadScanEnable), \ |
| UpperBits(hci::kReadScanEnable), \ |
| hci::kSuccess, (scan_enable)) |
| |
| const auto kReadScanEnableRspNone = READ_SCAN_ENABLE_RSP(0x00); |
| const auto kReadScanEnableRspInquiry = READ_SCAN_ENABLE_RSP(0x01); |
| const auto kReadScanEnableRspPage = READ_SCAN_ENABLE_RSP(0x02); |
| const auto kReadScanEnableRspBoth = READ_SCAN_ENABLE_RSP(0x03); |
| |
| #undef READ_SCAN_ENABLE_RSP |
| |
| #define WRITE_SCAN_ENABLE_CMD(scan_enable) \ |
| common::CreateStaticByteBuffer(LowerBits(hci::kWriteScanEnable), \ |
| UpperBits(hci::kWriteScanEnable), 0x01, \ |
| (scan_enable)) |
| |
| const auto kWriteScanEnableNone = WRITE_SCAN_ENABLE_CMD(0x00); |
| const auto kWriteScanEnableInq = WRITE_SCAN_ENABLE_CMD(0x01); |
| const auto kWriteScanEnablePage = WRITE_SCAN_ENABLE_CMD(0x02); |
| const auto kWriteScanEnableBoth = WRITE_SCAN_ENABLE_CMD(0x03); |
| |
| #undef WRITE_SCAN_ENABLE_CMD |
| |
| #define COMMAND_COMPLETE_RSP(opcode) \ |
| common::CreateStaticByteBuffer(hci::kCommandCompleteEventCode, 0x04, 0xF0, \ |
| LowerBits((opcode)), UpperBits((opcode)), \ |
| hci::kSuccess); |
| |
| const auto kWriteScanEnableRsp = COMMAND_COMPLETE_RSP(hci::kWriteScanEnable); |
| |
| const auto kWritePageScanActivity = common::CreateStaticByteBuffer( |
| LowerBits(hci::kWritePageScanActivity), |
| UpperBits(hci::kWritePageScanActivity), |
| 0x04, // parameter_total_size (4 bytes) |
| 0x00, 0x08, // 1.28s interval (R1) |
| 0x11, 0x00 // 10.625ms window (R1) |
| ); |
| |
| const auto kWritePageScanActivityRsp = |
| COMMAND_COMPLETE_RSP(hci::kWritePageScanActivity); |
| |
| const auto kWritePageScanType = common::CreateStaticByteBuffer( |
| LowerBits(hci::kWritePageScanType), UpperBits(hci::kWritePageScanType), |
| 0x01, // parameter_total_size (1 byte) |
| 0x01 // Interlaced scan |
| ); |
| |
| const auto kWritePageScanTypeRsp = |
| COMMAND_COMPLETE_RSP(hci::kWritePageScanType); |
| |
| |
| #define COMMAND_STATUS_RSP(opcode, statuscode) \ |
| common::CreateStaticByteBuffer(hci::kCommandStatusEventCode, 0x04, \ |
| (statuscode), 0xF0, \ |
| LowerBits((opcode)), UpperBits((opcode))); |
| // clang-format on |
| |
| const auto kConnectionRequest = common::CreateStaticByteBuffer( |
| hci::kConnectionRequestEventCode, |
| 0x0A, // parameter_total_size (10 byte payload) |
| TEST_DEV_ADDR_BYTES_LE, // peer address |
| 0x00, 0x1F, 0x00, // class_of_device (unspecified) |
| 0x01 // link_type (ACL) |
| ); |
| const auto kAcceptConnectionRequest = |
| common::CreateStaticByteBuffer(LowerBits(hci::kAcceptConnectionRequest), |
| UpperBits(hci::kAcceptConnectionRequest), |
| 0x07, // parameter_total_size (7 bytes) |
| TEST_DEV_ADDR_BYTES_LE, // peer address |
| 0x00 // role (become master) |
| ); |
| |
| const auto kAcceptConnectionRequestRsp = COMMAND_STATUS_RSP( |
| hci::kAcceptConnectionRequest, hci::StatusCode::kSuccess); |
| |
| const auto kConnectionComplete = common::CreateStaticByteBuffer( |
| hci::kConnectionCompleteEventCode, |
| 0x0B, // parameter_total_size (11 byte payload) |
| hci::StatusCode::kSuccess, // status |
| 0xAA, 0x0B, // connection_handle |
| TEST_DEV_ADDR_BYTES_LE, // peer address |
| 0x01, // link_type (ACL) |
| 0x00 // encryption not enabled |
| ); |
| const auto kRemoteNameRequest = common::CreateStaticByteBuffer( |
| LowerBits(hci::kRemoteNameRequest), UpperBits(hci::kRemoteNameRequest), |
| 0x0a, // parameter_total_size (10 bytes) |
| TEST_DEV_ADDR_BYTES_LE, // peer address |
| 0x00, // page_scan_repetition_mode (R0) |
| 0x00, // reserved |
| 0x00, 0x00 // clock_offset |
| ); |
| const auto kRemoteNameRequestRsp = |
| COMMAND_STATUS_RSP(hci::kRemoteNameRequest, hci::StatusCode::kSuccess); |
| |
| const auto kRemoteNameRequestComplete = common::CreateStaticByteBuffer( |
| hci::kRemoteNameRequestCompleteEventCode, |
| 0x20, // parameter_total_size (32) |
| hci::StatusCode::kSuccess, // status |
| TEST_DEV_ADDR_BYTES_LE, // peer address |
| 'F', 'u', 'c', 'h', 's', 'i', 'a', 0xF0, 0x9F, 0x92, 0x96, 0x00, 0x14, 0x15, |
| 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20 |
| // remote name (Fuchsia 💖) |
| // Everything after the 0x00 should be ignored. |
| ); |
| const auto kReadRemoteVersionInfo = |
| common::CreateStaticByteBuffer(LowerBits(hci::kReadRemoteVersionInfo), |
| UpperBits(hci::kReadRemoteVersionInfo), |
| 0x02, // Parameter_total_size (2 bytes) |
| 0xAA, 0x0B // connection_handle |
| ); |
| |
| const auto kReadRemoteVersionInfoRsp = |
| COMMAND_STATUS_RSP(hci::kReadRemoteVersionInfo, hci::StatusCode::kSuccess); |
| |
| const auto kRemoteVersionInfoComplete = |
| common::CreateStaticByteBuffer(hci::kReadRemoteVersionInfoCompleteEventCode, |
| 0x08, // parameter_total_size (8 bytes) |
| hci::StatusCode::kSuccess, // status |
| 0xAA, 0x0B, // connection_handle |
| hci::HCIVersion::k4_2, // lmp_version |
| 0xE0, 0x00, // manufacturer_name (Google) |
| 0xAD, 0xDE // lmp_subversion (anything) |
| ); |
| |
| const auto kReadRemoteSupportedFeatures = |
| common::CreateStaticByteBuffer(LowerBits(hci::kReadRemoteSupportedFeatures), |
| UpperBits(hci::kReadRemoteSupportedFeatures), |
| 0x02, // parameter_total_size (2 bytes) |
| 0xAA, 0x0B // connection_handle |
| ); |
| |
| const auto kReadRemoteSupportedFeaturesRsp = COMMAND_STATUS_RSP( |
| hci::kReadRemoteSupportedFeatures, hci::StatusCode::kSuccess); |
| |
| const auto kReadRemoteSupportedFeaturesComplete = |
| common::CreateStaticByteBuffer( |
| hci::kReadRemoteSupportedFeaturesCompleteEventCode, |
| 0x0B, // parameter_total_size (11 bytes) |
| hci::StatusCode::kSuccess, // status |
| 0xAA, 0x0B, // conneciton_handle, |
| 0xFF, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80 |
| // lmp_features |
| // Set: 3 slot packets, 5 slot packets, Encryption, Timing Accuracy, |
| // Role Switch, Hold Mode, Sniff Mode, LE Supported, Extended Features |
| ); |
| |
| const auto kReadRemoteExtended1 = |
| common::CreateStaticByteBuffer(LowerBits(hci::kReadRemoteExtendedFeatures), |
| UpperBits(hci::kReadRemoteExtendedFeatures), |
| 0x03, // parameter_total_size (3 bytes) |
| 0xAA, 0x0B, // connection_handle |
| 0x01 // page_number (1) |
| ); |
| |
| const auto kReadRemoteExtendedFeaturesRsp = COMMAND_STATUS_RSP( |
| hci::kReadRemoteExtendedFeatures, hci::StatusCode::kSuccess); |
| |
| const auto kReadRemoteExtended1Complete = common::CreateStaticByteBuffer( |
| hci::kReadRemoteExtendedFeaturesCompleteEventCode, |
| 0x0D, // parameter_total_size (13 bytes) |
| hci::StatusCode::kSuccess, // status |
| 0xAA, 0x0B, // connection_handle, |
| 0x01, // page_number |
| 0x03, // max_page_number (3 pages) |
| 0x0F, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 |
| // lmp_features |
| // Set: Secure Simple Pairing (Host Support), LE Supported (Host), |
| // SimultaneousLEAndBREDR, Secure Connections (Host Support) |
| ); |
| |
| const auto kReadRemoteExtended2 = |
| common::CreateStaticByteBuffer(LowerBits(hci::kReadRemoteExtendedFeatures), |
| UpperBits(hci::kReadRemoteExtendedFeatures), |
| 0x03, // parameter_total_size (3 bytes) |
| 0xAA, 0x0B, // connection_handle |
| 0x02 // page_number (2) |
| ); |
| |
| const auto kReadRemoteExtended2Complete = common::CreateStaticByteBuffer( |
| hci::kReadRemoteExtendedFeaturesCompleteEventCode, |
| 0x0D, // parameter_total_size (13 bytes) |
| hci::StatusCode::kSuccess, // status |
| 0xAA, 0x0B, // connection_handle, |
| 0x02, // page_number |
| 0x03, // max_page_number (3 pages) |
| 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xFF, 0x00 |
| // lmp_features - All the bits should be ignored. |
| ); |
| |
| const auto kDisconnect = common::CreateStaticByteBuffer( |
| LowerBits(hci::kDisconnect), UpperBits(hci::kDisconnect), |
| 0x03, // parameter_total_size (3 bytes) |
| 0xAA, 0x0B, // connection_handle |
| 0x13 // Reason (Remote User Terminated Connection) |
| ); |
| const auto kDisconnectRsp = |
| COMMAND_STATUS_RSP(hci::kDisconnect, hci::StatusCode::kSuccess); |
| |
| const auto kDisconnectionComplete = common::CreateStaticByteBuffer( |
| hci::kDisconnectionCompleteEventCode, |
| 0x04, // parameter_total_size (4 bytes) |
| hci::StatusCode::kSuccess, // status |
| 0xAA, 0x0B, // connection_handle |
| 0x13 // Reason (Remote User Terminated Connection) |
| ); |
| |
| class BrEdrConnectionManagerTest : public TestingBase { |
| public: |
| BrEdrConnectionManagerTest() = default; |
| ~BrEdrConnectionManagerTest() override = default; |
| |
| void SetUp() override { |
| TestingBase::SetUp(); |
| InitializeACLDataChannel(); |
| |
| device_cache_ = std::make_unique<RemoteDeviceCache>(); |
| data_domain_ = data::testing::FakeDomain::Create(); |
| data_domain_->Initialize(); |
| connection_manager_ = std::make_unique<BrEdrConnectionManager>( |
| transport(), device_cache_.get(), data_domain_, true); |
| |
| StartTestDevice(); |
| |
| test_device()->SetTransactionCallback([this] { transaction_count_++; }, |
| async_get_default_dispatcher()); |
| } |
| |
| void TearDown() override { |
| // Don't trigger the transaction callback when cleaning up the manager. |
| test_device()->ClearTransactionCallback(); |
| if (connection_manager_ != nullptr) { |
| // deallocating the connection manager disables connectivity. |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kReadScanEnable, {&kReadScanEnableRspBoth})); |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kWriteScanEnableInq, {&kWriteScanEnableRsp})); |
| connection_manager_ = nullptr; |
| } |
| RunLoopUntilIdle(); |
| test_device()->Stop(); |
| data_domain_ = nullptr; |
| device_cache_ = nullptr; |
| TestingBase::TearDown(); |
| } |
| |
| protected: |
| BrEdrConnectionManager* connmgr() const { return connection_manager_.get(); } |
| void SetConnectionManager(std::unique_ptr<BrEdrConnectionManager> mgr) { |
| connection_manager_ = std::move(mgr); |
| } |
| |
| RemoteDeviceCache* device_cache() const { return device_cache_.get(); } |
| |
| data::testing::FakeDomain* data_domain() const { return data_domain_.get(); } |
| |
| int transaction_count() const { return transaction_count_; } |
| |
| void QueueSuccessfulIncomingConn() const { |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kAcceptConnectionRequest, |
| {&kAcceptConnectionRequestRsp, &kConnectionComplete})); |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kRemoteNameRequest, |
| {&kRemoteNameRequestRsp, &kRemoteNameRequestComplete})); |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kReadRemoteVersionInfo, |
| {&kReadRemoteVersionInfoRsp, &kRemoteVersionInfoComplete})); |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kReadRemoteSupportedFeatures, {&kReadRemoteSupportedFeaturesRsp, |
| &kReadRemoteSupportedFeaturesComplete})); |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kReadRemoteExtended1, |
| {&kReadRemoteExtendedFeaturesRsp, &kReadRemoteExtended1Complete})); |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kReadRemoteExtended2, |
| {&kReadRemoteExtendedFeaturesRsp, &kReadRemoteExtended2Complete})); |
| } |
| |
| void QueueDisconnection() const { |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kDisconnect, {&kDisconnectRsp, &kDisconnectionComplete})); |
| } |
| |
| private: |
| std::unique_ptr<BrEdrConnectionManager> connection_manager_; |
| std::unique_ptr<RemoteDeviceCache> device_cache_; |
| fbl::RefPtr<data::testing::FakeDomain> data_domain_; |
| int transaction_count_ = 0; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(BrEdrConnectionManagerTest); |
| }; |
| |
| using GAP_BrEdrConnectionManagerTest = BrEdrConnectionManagerTest; |
| |
| TEST_F(GAP_BrEdrConnectionManagerTest, DisableConnectivity) { |
| size_t cb_count = 0; |
| auto cb = [&cb_count](const auto& status) { |
| cb_count++; |
| EXPECT_TRUE(status); |
| }; |
| |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kReadScanEnable, {&kReadScanEnableRspPage})); |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kWriteScanEnableNone, {&kWriteScanEnableRsp})); |
| |
| connmgr()->SetConnectable(false, cb); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(1u, cb_count); |
| |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kReadScanEnable, {&kReadScanEnableRspBoth})); |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kWriteScanEnableInq, {&kWriteScanEnableRsp})); |
| |
| connmgr()->SetConnectable(false, cb); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(2u, cb_count); |
| } |
| |
| TEST_F(GAP_BrEdrConnectionManagerTest, EnableConnectivity) { |
| size_t cb_count = 0; |
| auto cb = [&cb_count](const auto& status) { |
| cb_count++; |
| EXPECT_TRUE(status); |
| }; |
| |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kWritePageScanActivity, {&kWritePageScanActivityRsp})); |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kWritePageScanType, {&kWritePageScanTypeRsp})); |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kReadScanEnable, {&kReadScanEnableRspNone})); |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kWriteScanEnablePage, {&kWriteScanEnableRsp})); |
| |
| connmgr()->SetConnectable(true, cb); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(1u, cb_count); |
| |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kWritePageScanActivity, {&kWritePageScanActivityRsp})); |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kWritePageScanType, {&kWritePageScanTypeRsp})); |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kReadScanEnable, {&kReadScanEnableRspInquiry})); |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kWriteScanEnableBoth, {&kWriteScanEnableRsp})); |
| |
| connmgr()->SetConnectable(true, cb); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(2u, cb_count); |
| } |
| |
| // Test: An incoming connection request should trigger an acceptance and |
| // interrogation should allow a device that only report the first Extended |
| // Features page. |
| TEST_F(GAP_BrEdrConnectionManagerTest, |
| IncomingConnection_BrokenExtendedPageResponse) { |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kAcceptConnectionRequest, |
| {&kAcceptConnectionRequestRsp, &kConnectionComplete})); |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kRemoteNameRequest, |
| {&kRemoteNameRequestRsp, &kRemoteNameRequestComplete})); |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kReadRemoteVersionInfo, |
| {&kReadRemoteVersionInfoRsp, &kRemoteVersionInfoComplete})); |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kReadRemoteSupportedFeatures, {&kReadRemoteSupportedFeaturesRsp, |
| &kReadRemoteSupportedFeaturesComplete})); |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kReadRemoteExtended1, |
| {&kReadRemoteExtendedFeaturesRsp, &kReadRemoteExtended1Complete})); |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kReadRemoteExtended2, |
| {&kReadRemoteExtendedFeaturesRsp, &kReadRemoteExtended1Complete})); |
| |
| test_device()->SendCommandChannelPacket(kConnectionRequest); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(6, transaction_count()); |
| |
| // When we deallocate the connection manager next, we should disconnect. |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kDisconnect, {&kDisconnectRsp, &kDisconnectionComplete})); |
| |
| // deallocating the connection manager disables connectivity. |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kReadScanEnable, {&kReadScanEnableRspBoth})); |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kWriteScanEnableInq, {&kWriteScanEnableRsp})); |
| |
| SetConnectionManager(nullptr); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(9, transaction_count()); |
| } |
| |
| // Test: An incoming connection request should trigger an acceptance and an |
| // interrogation to discover capabilities. |
| TEST_F(GAP_BrEdrConnectionManagerTest, IncomingConnectionSuccess) { |
| EXPECT_EQ("", connmgr()->GetPeerId(kConnectionHandle)); |
| |
| QueueSuccessfulIncomingConn(); |
| |
| test_device()->SendCommandChannelPacket(kConnectionRequest); |
| |
| RunLoopUntilIdle(); |
| |
| auto* dev = device_cache()->FindDeviceByAddress(kTestDevAddr); |
| ASSERT_TRUE(dev); |
| EXPECT_EQ(dev->identifier(), connmgr()->GetPeerId(kConnectionHandle)); |
| |
| EXPECT_EQ(6, transaction_count()); |
| |
| // When we deallocate the connection manager next, we should disconnect. |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kDisconnect, {&kDisconnectRsp, &kDisconnectionComplete})); |
| |
| // deallocating the connection manager disables connectivity. |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kReadScanEnable, {&kReadScanEnableRspBoth})); |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kWriteScanEnableInq, {&kWriteScanEnableRsp})); |
| |
| SetConnectionManager(nullptr); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(9, transaction_count()); |
| } |
| |
| // Test: An incoming connection request should upgrade a known LE device with a |
| // matching address to a dual mode device. |
| TEST_F(GAP_BrEdrConnectionManagerTest, |
| IncomingConnectionUpgradesKnownLowEnergyDeviceToDualMode) { |
| const DeviceAddress le_alias_addr(DeviceAddress::Type::kLEPublic, |
| kTestDevAddr.value()); |
| RemoteDevice* const dev = device_cache()->NewDevice(le_alias_addr, true); |
| ASSERT_TRUE(dev); |
| ASSERT_EQ(TechnologyType::kLowEnergy, dev->technology()); |
| |
| QueueSuccessfulIncomingConn(); |
| |
| test_device()->SendCommandChannelPacket(kConnectionRequest); |
| |
| RunLoopUntilIdle(); |
| |
| ASSERT_EQ(dev, device_cache()->FindDeviceByAddress(kTestDevAddr)); |
| EXPECT_EQ(dev->identifier(), connmgr()->GetPeerId(kConnectionHandle)); |
| EXPECT_EQ(TechnologyType::kDualMode, dev->technology()); |
| |
| // Prepare for disconnection upon teardown. |
| QueueDisconnection(); |
| } |
| |
| // Test: A remote disconnect should correctly remove the conneection. |
| TEST_F(GAP_BrEdrConnectionManagerTest, RemoteDisconnect) { |
| EXPECT_EQ("", connmgr()->GetPeerId(kConnectionHandle)); |
| |
| QueueSuccessfulIncomingConn(); |
| |
| test_device()->SendCommandChannelPacket(kConnectionRequest); |
| |
| RunLoopUntilIdle(); |
| |
| auto* dev = device_cache()->FindDeviceByAddress(kTestDevAddr); |
| ASSERT_TRUE(dev); |
| EXPECT_EQ(dev->identifier(), connmgr()->GetPeerId(kConnectionHandle)); |
| |
| EXPECT_EQ(6, transaction_count()); |
| |
| // Remote end disconnects. |
| test_device()->SendCommandChannelPacket(kDisconnectionComplete); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ("", connmgr()->GetPeerId(kConnectionHandle)); |
| |
| // deallocating the connection manager disables connectivity. |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kReadScanEnable, {&kReadScanEnableRspBoth})); |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kWriteScanEnableInq, {&kWriteScanEnableRsp})); |
| |
| SetConnectionManager(nullptr); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(8, transaction_count()); |
| } |
| |
| const auto kRemoteNameRequestCompleteFailed = |
| common::CreateStaticByteBuffer(hci::kRemoteNameRequestCompleteEventCode, |
| 0x01, // parameter_total_size (1 bytes) |
| hci::StatusCode::kHardwareFailure); |
| |
| const auto kReadRemoteSupportedFeaturesCompleteFailed = |
| common::CreateStaticByteBuffer(hci::kRemoteNameRequestCompleteEventCode, |
| 0x01, // parameter_total_size (1 bytes) |
| hci::StatusCode::kHardwareFailure); |
| |
| // Test: if the interrogation fails, we disconnect. |
| // - Receiving extra responses after a command fails will not fail |
| // - We don't query extended features if we don't receive an answer. |
| TEST_F(GAP_BrEdrConnectionManagerTest, IncommingConnectionFailedInterrogation) { |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kAcceptConnectionRequest, |
| {&kAcceptConnectionRequestRsp, &kConnectionComplete})); |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kRemoteNameRequest, |
| {&kRemoteNameRequestRsp, &kRemoteNameRequestCompleteFailed})); |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kReadRemoteVersionInfo, |
| {&kReadRemoteVersionInfoRsp, &kRemoteVersionInfoComplete})); |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kReadRemoteSupportedFeatures, |
| {&kReadRemoteSupportedFeaturesRsp, |
| &kReadRemoteSupportedFeaturesCompleteFailed})); |
| |
| test_device()->QueueCommandTransaction(CommandTransaction( |
| kDisconnect, {&kDisconnectRsp, &kDisconnectionComplete})); |
| |
| test_device()->SendCommandChannelPacket(kConnectionRequest); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(5, transaction_count()); |
| } |
| |
| const auto kCapabilitiesRequest = |
| common::CreateStaticByteBuffer(hci::kIOCapabilityRequestEventCode, |
| 0x06, // parameter_total_size (6 bytes) |
| TEST_DEV_ADDR_BYTES_LE // address |
| ); |
| |
| const auto kCapabilitiesRequestReply = common::CreateStaticByteBuffer( |
| LowerBits(hci::kIOCapabilityRequestReply), |
| UpperBits(hci::kIOCapabilityRequestReply), |
| 0x09, // parameter_total_size (9 bytes) |
| TEST_DEV_ADDR_BYTES_LE, // peer address |
| 0x03, // No input, No output |
| 0x00, // No OOB data present |
| 0x04 // MITM Protection Not Required – General Bonding |
| ); |
| |
| const auto kCapabilitiesRequestReplyRsp = |
| common::CreateStaticByteBuffer(hci::kCommandCompleteEventCode, 0x0A, 0xF0, |
| LowerBits(hci::kIOCapabilityRequestReply), |
| UpperBits(hci::kIOCapabilityRequestReply), |
| hci::kSuccess, // status |
| TEST_DEV_ADDR_BYTES_LE // peer address |
| ); |
| |
| // Test: sends replies to Capability Requests |
| // TODO(jamuraa): returns correct capabilities when we have different |
| // requirements. |
| TEST_F(GAP_BrEdrConnectionManagerTest, CapabilityRequest) { |
| test_device()->QueueCommandTransaction(kCapabilitiesRequestReply, |
| {&kCapabilitiesRequestReplyRsp}); |
| |
| test_device()->SendCommandChannelPacket(kCapabilitiesRequest); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(1, transaction_count()); |
| } |
| |
| const auto kUserConfirmationRequest = common::CreateStaticByteBuffer( |
| hci::kUserConfirmationRequestEventCode, |
| 0x0A, // parameter_total_size (10 byte payload) |
| TEST_DEV_ADDR_BYTES_LE, // peer address |
| 0x00, 0x00, 0x00, 0x00 // numeric value 000000 |
| ); |
| |
| const auto kConfirmationRequestReply = common::CreateStaticByteBuffer( |
| LowerBits(hci::kUserConfirmationRequestReply), |
| UpperBits(hci::kUserConfirmationRequestReply), |
| 0x06, // parameter_total_size (6 bytes) |
| TEST_DEV_ADDR_BYTES_LE // peer address |
| ); |
| |
| const auto kConfirmationRequestReplyRsp = |
| COMMAND_COMPLETE_RSP(hci::kUserConfirmationRequestReply); |
| |
| // Test: sends replies to Confirmation Requests |
| TEST_F(GAP_BrEdrConnectionManagerTest, ConfirmationRequest) { |
| test_device()->QueueCommandTransaction(kConfirmationRequestReply, |
| {&kConfirmationRequestReplyRsp}); |
| |
| test_device()->SendCommandChannelPacket(kUserConfirmationRequest); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(1, transaction_count()); |
| } |
| |
| const auto kLinkKeyRequest = |
| common::CreateStaticByteBuffer(hci::kLinkKeyRequestEventCode, |
| 0x06, // parameter_total_size (6 bytes) |
| TEST_DEV_ADDR_BYTES_LE // peer address |
| ); |
| |
| const auto kLinkKeyRequestNegativeReply = |
| common::CreateStaticByteBuffer(LowerBits(hci::kLinkKeyRequestNegativeReply), |
| UpperBits(hci::kLinkKeyRequestNegativeReply), |
| 0x06, // parameter_total_size (6 bytes) |
| TEST_DEV_ADDR_BYTES_LE // peer address |
| ); |
| |
| const auto kLinkKeyRequestNegativeReplyRsp = |
| common::CreateStaticByteBuffer(hci::kCommandCompleteEventCode, 0x0A, 0xF0, |
| LowerBits(hci::kLinkKeyRequestNegativeReply), |
| UpperBits(hci::kLinkKeyRequestNegativeReply), |
| hci::kSuccess, // status |
| TEST_DEV_ADDR_BYTES_LE // peer address |
| ); |
| |
| // Test: replies negative to Link Key Requests for unknown and unbonded devices |
| TEST_F(GAP_BrEdrConnectionManagerTest, LinkKeyRequestAndNegativeReply) { |
| test_device()->QueueCommandTransaction(kLinkKeyRequestNegativeReply, |
| {&kLinkKeyRequestNegativeReplyRsp}); |
| |
| test_device()->SendCommandChannelPacket(kLinkKeyRequest); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(1, transaction_count()); |
| |
| QueueSuccessfulIncomingConn(); |
| |
| test_device()->SendCommandChannelPacket(kConnectionRequest); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(7, transaction_count()); |
| |
| auto* dev = device_cache()->FindDeviceByAddress(kTestDevAddr); |
| ASSERT_TRUE(dev); |
| ASSERT_TRUE(dev->connected()); |
| ASSERT_FALSE(dev->bonded()); |
| |
| test_device()->QueueCommandTransaction(kLinkKeyRequestNegativeReply, |
| {&kLinkKeyRequestNegativeReplyRsp}); |
| |
| test_device()->SendCommandChannelPacket(kLinkKeyRequest); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(8, transaction_count()); |
| |
| QueueDisconnection(); |
| } |
| |
| const auto kLinkKeyNotification = common::CreateStaticByteBuffer( |
| hci::kLinkKeyNotificationEventCode, |
| 0x17, // parameter_total_size (17 bytes) |
| TEST_DEV_ADDR_BYTES_LE, // peer address |
| 0xc0, 0xde, 0xfa, 0x57, 0x4b, 0xad, 0xf0, 0x0d, 0xa7, 0x60, 0x06, 0x1e, |
| 0xca, 0x1e, 0xca, 0xfe, // link key |
| 0x04 // key type (Unauthenticated Combination Key generated from P-192) |
| ); |
| |
| const auto kLinkKeyRequestReply = common::CreateStaticByteBuffer( |
| LowerBits(hci::kLinkKeyRequestReply), UpperBits(hci::kLinkKeyRequestReply), |
| 0x16, // parameter_total_size (22 bytes) |
| TEST_DEV_ADDR_BYTES_LE, // peer address |
| 0xc0, 0xde, 0xfa, 0x57, 0x4b, 0xad, 0xf0, 0x0d, 0xa7, 0x60, 0x06, 0x1e, |
| 0xca, 0x1e, 0xca, 0xfe // link key |
| ); |
| |
| const auto kLinkKeyRequestReplyRsp = common::CreateStaticByteBuffer( |
| hci::kCommandCompleteEventCode, 0x0A, 0xF0, |
| LowerBits(hci::kLinkKeyRequestReply), UpperBits(hci::kLinkKeyRequestReply), |
| hci::kSuccess, // status |
| TEST_DEV_ADDR_BYTES_LE // peer address |
| ); |
| |
| const auto kLinkKeyNotificationChanged = common::CreateStaticByteBuffer( |
| hci::kLinkKeyNotificationEventCode, |
| 0x17, // parameter_total_size (17 bytes) |
| TEST_DEV_ADDR_BYTES_LE, // peer address |
| 0xfa, 0xce, 0xb0, 0x0c, 0xa5, 0x1c, 0xcd, 0x15, 0xea, 0x5e, 0xfe, 0xdb, |
| 0x1d, 0x0d, 0x0a, 0xd5, // link key |
| 0x06 // key type (Changed Combination Key) |
| ); |
| |
| const auto kLinkKeyRequestReplyChanged = common::CreateStaticByteBuffer( |
| LowerBits(hci::kLinkKeyRequestReply), UpperBits(hci::kLinkKeyRequestReply), |
| 0x16, // parameter_total_size (22 bytes) |
| TEST_DEV_ADDR_BYTES_LE, // peer address |
| 0xfa, 0xce, 0xb0, 0x0c, 0xa5, 0x1c, 0xcd, 0x15, 0xea, 0x5e, 0xfe, 0xdb, |
| 0x1d, 0x0d, 0x0a, 0xd5 // link key |
| ); |
| |
| // Test: stores and recalls link key for a remote device |
| TEST_F(GAP_BrEdrConnectionManagerTest, BondRemoteDevice) { |
| QueueSuccessfulIncomingConn(); |
| |
| test_device()->SendCommandChannelPacket(kConnectionRequest); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(6, transaction_count()); |
| |
| auto* dev = device_cache()->FindDeviceByAddress(kTestDevAddr); |
| ASSERT_TRUE(dev); |
| ASSERT_TRUE(dev->connected()); |
| ASSERT_FALSE(dev->bonded()); |
| |
| test_device()->SendCommandChannelPacket(kLinkKeyNotification); |
| |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(dev->bonded()); |
| |
| test_device()->QueueCommandTransaction(kLinkKeyRequestReply, |
| {&kLinkKeyRequestReplyRsp}); |
| |
| test_device()->SendCommandChannelPacket(kLinkKeyRequest); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(7, transaction_count()); |
| |
| // Change the link key. |
| test_device()->SendCommandChannelPacket(kLinkKeyNotificationChanged); |
| |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(dev->bonded()); |
| |
| test_device()->QueueCommandTransaction(kLinkKeyRequestReplyChanged, |
| {&kLinkKeyRequestReplyRsp}); |
| |
| test_device()->SendCommandChannelPacket(kLinkKeyRequest); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_TRUE(dev->bonded()); |
| EXPECT_EQ(8, transaction_count()); |
| |
| QueueDisconnection(); |
| } |
| |
| // Test: can't change the link key of an unbonded device |
| TEST_F(GAP_BrEdrConnectionManagerTest, UnbondedDeviceChangeLinkKey) { |
| QueueSuccessfulIncomingConn(); |
| |
| test_device()->SendCommandChannelPacket(kConnectionRequest); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(6, transaction_count()); |
| |
| auto* dev = device_cache()->FindDeviceByAddress(kTestDevAddr); |
| ASSERT_TRUE(dev); |
| ASSERT_TRUE(dev->connected()); |
| ASSERT_FALSE(dev->bonded()); |
| |
| // Change the link key. |
| test_device()->SendCommandChannelPacket(kLinkKeyNotificationChanged); |
| |
| RunLoopUntilIdle(); |
| EXPECT_FALSE(dev->bonded()); |
| |
| test_device()->QueueCommandTransaction(kLinkKeyRequestNegativeReply, |
| {&kLinkKeyRequestReplyRsp}); |
| |
| test_device()->SendCommandChannelPacket(kLinkKeyRequest); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_FALSE(dev->bonded()); |
| EXPECT_EQ(7, transaction_count()); |
| |
| QueueDisconnection(); |
| } |
| |
| const auto kLinkKeyNotificationLegacy = common::CreateStaticByteBuffer( |
| hci::kLinkKeyNotificationEventCode, |
| 0x17, // parameter_total_size (17 bytes) |
| TEST_DEV_ADDR_BYTES_LE, // peer address |
| 0x41, 0x33, 0x7c, 0x0d, 0xef, 0xee, 0xda, 0xda, 0xba, 0xad, 0x0f, 0xf1, |
| 0xce, 0xc0, 0xff, 0xee, // link key |
| 0x00 // key type (Combination Key) |
| ); |
| |
| // Test: don't bond if the link key resulted from legacy pairing |
| TEST_F(GAP_BrEdrConnectionManagerTest, LegacyLinkKeyNotBonded) { |
| QueueSuccessfulIncomingConn(); |
| |
| test_device()->SendCommandChannelPacket(kConnectionRequest); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(6, transaction_count()); |
| |
| auto* dev = device_cache()->FindDeviceByAddress(kTestDevAddr); |
| ASSERT_TRUE(dev); |
| ASSERT_TRUE(dev->connected()); |
| ASSERT_FALSE(dev->bonded()); |
| |
| test_device()->SendCommandChannelPacket(kLinkKeyNotificationLegacy); |
| |
| RunLoopUntilIdle(); |
| EXPECT_FALSE(dev->bonded()); |
| |
| test_device()->QueueCommandTransaction(kLinkKeyRequestNegativeReply, |
| {&kLinkKeyRequestReplyRsp}); |
| |
| test_device()->SendCommandChannelPacket(kLinkKeyRequest); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_FALSE(dev->bonded()); |
| EXPECT_EQ(7, transaction_count()); |
| |
| QueueDisconnection(); |
| } |
| |
| // Test: if L2CAP gets a link error, we disconnect the connection |
| TEST_F(GAP_BrEdrConnectionManagerTest, DisconnectOnLinkError) { |
| QueueSuccessfulIncomingConn(); |
| |
| test_device()->SendCommandChannelPacket(kConnectionRequest); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(6, transaction_count()); |
| |
| // When we deallocate the connection manager next, we should disconnect. |
| QueueDisconnection(); |
| |
| data_domain()->TriggerLinkError(kConnectionHandle); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(7, transaction_count()); |
| |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kReadScanEnable, {&kReadScanEnableRspBoth})); |
| test_device()->QueueCommandTransaction( |
| CommandTransaction(kWriteScanEnableInq, {&kWriteScanEnableRsp})); |
| |
| SetConnectionManager(nullptr); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(9, transaction_count()); |
| } |
| |
| TEST_F(GAP_BrEdrConnectionManagerTest, ConnectedDeviceTimeout) { |
| QueueSuccessfulIncomingConn(); |
| |
| test_device()->SendCommandChannelPacket(kConnectionRequest); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(6, transaction_count()); |
| |
| auto* dev = device_cache()->FindDeviceByAddress(kTestDevAddr); |
| ASSERT_TRUE(dev); |
| EXPECT_TRUE(dev->connected()); |
| |
| // We want to make sure the connection doesn't expire. |
| RunLoopFor(zx::sec(600)); |
| |
| // Remote end disconnects. |
| test_device()->SendCommandChannelPacket(kDisconnectionComplete); |
| |
| RunLoopUntilIdle(); |
| |
| // Device should still be there, but not connected anymore |
| dev = device_cache()->FindDeviceByAddress(kTestDevAddr); |
| ASSERT_TRUE(dev); |
| EXPECT_FALSE(dev->connected()); |
| |
| EXPECT_EQ("", connmgr()->GetPeerId(kConnectionHandle)); |
| } |
| |
| #undef COMMAND_COMPLETE_RSP |
| #undef COMMAND_STATUS_RSP |
| |
| } // namespace |
| } // namespace gap |
| } // namespace btlib |