| // 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 "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/low_energy_address_manager.h" |
| |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/gap.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/sm/util.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/controller_test.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/mock_controller.h" |
| |
| namespace bt::gap { |
| namespace { |
| |
| using testing::CommandTransaction; |
| using testing::MockController; |
| |
| using TestingBase = testing::FakeDispatcherControllerTest<MockController>; |
| |
| const DeviceAddress kPublic(DeviceAddress::Type::kLEPublic, |
| {0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA}); |
| |
| class LowEnergyAddressManagerTest : public TestingBase { |
| public: |
| LowEnergyAddressManagerTest() = default; |
| ~LowEnergyAddressManagerTest() override = default; |
| |
| protected: |
| void SetUp() override { |
| TestingBase::SetUp(); |
| addr_mgr_ = std::make_unique<LowEnergyAddressManager>( |
| kPublic, |
| [this] { return IsRandomAddressChangeAllowed(); }, |
| cmd_channel()->AsWeakPtr(), |
| dispatcher()); |
| ASSERT_EQ(kPublic, addr_mgr()->identity_address()); |
| ASSERT_FALSE(addr_mgr()->irk()); |
| addr_mgr_->register_address_changed_callback( |
| [&](auto) { address_changed_cb_count_++; }); |
| } |
| |
| void TearDown() override { |
| addr_mgr_ = nullptr; |
| TestingBase::TearDown(); |
| } |
| |
| DeviceAddress EnsureLocalAddress() { |
| bool called = false; |
| DeviceAddress result; |
| addr_mgr()->EnsureLocalAddress([&](const auto& addr) { |
| result = addr; |
| called = true; |
| }); |
| RunUntilIdle(); |
| EXPECT_TRUE(called); |
| return result; |
| } |
| |
| // Called by |addr_mgr_|. |
| bool IsRandomAddressChangeAllowed() const { |
| return random_address_change_allowed_; |
| } |
| |
| LowEnergyAddressManager* addr_mgr() const { return addr_mgr_.get(); } |
| |
| void set_random_address_change_allowed(bool value) { |
| random_address_change_allowed_ = value; |
| } |
| |
| size_t address_changed_cb_count() const { return address_changed_cb_count_; } |
| |
| private: |
| std::unique_ptr<LowEnergyAddressManager> addr_mgr_; |
| bool random_address_change_allowed_ = true; |
| size_t address_changed_cb_count_ = 0; |
| |
| BT_DISALLOW_COPY_ASSIGN_AND_MOVE(LowEnergyAddressManagerTest); |
| }; |
| |
| TEST_F(LowEnergyAddressManagerTest, DefaultState) { |
| EXPECT_EQ(kPublic, EnsureLocalAddress()); |
| } |
| |
| TEST_F(LowEnergyAddressManagerTest, EnablePrivacy) { |
| // Respond with success. |
| const StaticByteBuffer kResponse(0x0E, |
| 4, // Command Complete, 4 bytes, |
| 1, // 1 allowed packet |
| 0x05, |
| 0x20, // opcode: HCI_LE_Set_Random_Address |
| 0x00 // status: success |
| ); |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), hci_spec::kLESetRandomAddress, &kResponse); |
| |
| const UInt128 kIrk{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}; |
| int hci_cmd_count = 0; |
| DeviceAddress addr; |
| test_device()->SetTransactionCallback([&](const auto& rx) { |
| hci_cmd_count++; |
| |
| const auto addr_bytes = rx.view(sizeof(hci_spec::CommandHeader)); |
| ASSERT_EQ(6u, addr_bytes.size()); |
| addr = DeviceAddress(DeviceAddress::Type::kLERandom, |
| DeviceAddressBytes(addr_bytes)); |
| }); |
| |
| addr_mgr()->set_irk(kIrk); |
| ASSERT_TRUE(addr_mgr()->irk()); |
| EXPECT_EQ(kIrk, *addr_mgr()->irk()); |
| EXPECT_FALSE(addr_mgr()->PrivacyEnabled()); |
| |
| addr_mgr()->EnablePrivacy(true); |
| // Privacy is now considered enabled. |
| // Even though Privacy is enabled, the LE address has not been changed so no |
| // notifications. |
| EXPECT_EQ(address_changed_cb_count(), 0u); |
| // Further requests to enable should not trigger additional HCI commands. |
| addr_mgr()->EnablePrivacy(true); |
| RunUntilIdle(); |
| |
| EXPECT_TRUE(addr_mgr()->PrivacyEnabled()); |
| // We should have received a HCI command with a RPA resolvable using |kIrk|. |
| EXPECT_EQ(1, hci_cmd_count); |
| EXPECT_TRUE(addr.IsResolvablePrivate()); |
| EXPECT_TRUE(sm::util::IrkCanResolveRpa(kIrk, addr)); |
| // The new random address should be returned. |
| EXPECT_EQ(addr, EnsureLocalAddress()); |
| // The address is updated so the listener should be notified. |
| EXPECT_EQ(address_changed_cb_count(), 1u); |
| |
| // Assign a new IRK. The new address should be used when it gets refreshed. |
| // Re-enable privacy with a new IRK. The latest IRK should be used. |
| const UInt128 kIrk2{{15, 14, 14, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}}; |
| addr_mgr()->set_irk(kIrk2); |
| ASSERT_TRUE(addr_mgr()->irk()); |
| EXPECT_EQ(kIrk2, *addr_mgr()->irk()); |
| |
| // Returns the same address. |
| EXPECT_EQ(addr, EnsureLocalAddress()); |
| EXPECT_FALSE(sm::util::IrkCanResolveRpa(kIrk2, addr)); |
| |
| // Re-enable privacy to trigger a refresh. |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), hci_spec::kLESetRandomAddress, &kResponse); |
| addr_mgr()->EnablePrivacy(false); |
| // Disabling Privacy should result in the Public address being used so we |
| // expect a notification. |
| EXPECT_EQ(address_changed_cb_count(), 2u); |
| addr_mgr()->EnablePrivacy(true); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(addr, EnsureLocalAddress()); |
| EXPECT_TRUE(addr.IsResolvablePrivate()); |
| EXPECT_TRUE(sm::util::IrkCanResolveRpa(kIrk2, addr)); |
| EXPECT_EQ(address_changed_cb_count(), 3u); |
| } |
| |
| TEST_F(LowEnergyAddressManagerTest, EnablePrivacyNoIrk) { |
| // Respond with success. |
| const StaticByteBuffer kResponse(0x0E, |
| 4, // Command Complete, 4 bytes, |
| 1, // 1 allowed packet |
| 0x05, |
| 0x20, // opcode: HCI_LE_Set_Random_Address |
| 0x00 // status: success |
| ); |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), hci_spec::kLESetRandomAddress, &kResponse); |
| |
| int hci_cmd_count = 0; |
| DeviceAddress addr; |
| test_device()->SetTransactionCallback([&](const auto& rx) { |
| hci_cmd_count++; |
| |
| const auto addr_bytes = rx.view(sizeof(hci_spec::CommandHeader)); |
| ASSERT_EQ(6u, addr_bytes.size()); |
| addr = DeviceAddress(DeviceAddress::Type::kLERandom, |
| DeviceAddressBytes(addr_bytes)); |
| }); |
| |
| addr_mgr()->EnablePrivacy(true); |
| |
| // Privacy is now considered enabled. Further requests to enable should not |
| // trigger additional HCI commands. |
| EXPECT_EQ(address_changed_cb_count(), 0u); |
| addr_mgr()->EnablePrivacy(true); |
| RunUntilIdle(); |
| |
| // We should have received a HCI command with a NRPA. |
| EXPECT_EQ(1, hci_cmd_count); |
| EXPECT_TRUE(addr.IsNonResolvablePrivate()); |
| |
| // The new random address should be returned. |
| EXPECT_EQ(addr, EnsureLocalAddress()); |
| EXPECT_EQ(address_changed_cb_count(), 1u); |
| } |
| |
| TEST_F(LowEnergyAddressManagerTest, EnablePrivacyHciError) { |
| // Respond with error. |
| const StaticByteBuffer kErrorResponse( |
| 0x0E, |
| 4, // Command Complete, 4 bytes, |
| 1, // 1 allowed packet |
| 0x05, |
| 0x20, // opcode: HCI_LE_Set_Random_Address |
| 0x0C // status: Command Disallowed |
| ); |
| // The second time respond with success. |
| const StaticByteBuffer kSuccessResponse( |
| 0x0E, |
| 4, // Command Complete, 4 bytes, |
| 1, // 1 allowed packet |
| 0x05, |
| 0x20, // opcode: HCI_LE_Set_Random_Address |
| 0x00 // status: success |
| ); |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), hci_spec::kLESetRandomAddress, &kErrorResponse); |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), hci_spec::kLESetRandomAddress, &kSuccessResponse); |
| |
| addr_mgr()->EnablePrivacy(true); |
| |
| // Request the new address and run the event loop. The old address should be |
| // returned due to the failure. |
| EXPECT_EQ(kPublic, EnsureLocalAddress()); |
| // Address hasn't changed so no notifications. |
| EXPECT_EQ(address_changed_cb_count(), 0u); |
| |
| // Requesting the address a second time while address update is disallowed |
| // should return the old address without sending HCI commands. |
| int hci_count = 0; |
| test_device()->SetTransactionCallback([&] { hci_count++; }); |
| set_random_address_change_allowed(false); |
| EXPECT_EQ(kPublic, EnsureLocalAddress()); |
| EXPECT_EQ(0, hci_count); |
| // Address hasn't changed so no notifications. |
| EXPECT_EQ(address_changed_cb_count(), 0u); |
| |
| // Requesting the address a third time while address update is allowed should |
| // configure and return the new address. |
| set_random_address_change_allowed(true); |
| EXPECT_TRUE(EnsureLocalAddress().IsNonResolvablePrivate()); |
| EXPECT_EQ(1, hci_count); |
| EXPECT_EQ(address_changed_cb_count(), 1u); |
| } |
| |
| TEST_F(LowEnergyAddressManagerTest, |
| EnablePrivacyWhileAddressChangeIsDisallowed) { |
| const auto kSuccessResponse = |
| StaticByteBuffer(0x0E, |
| 4, // Command Complete, 4 bytes, |
| 1, // 1 allowed packet |
| 0x05, |
| 0x20, // opcode: HCI_LE_Set_Random_Address |
| 0x00 // status: success |
| ); |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), hci_spec::kLESetRandomAddress, &kSuccessResponse); |
| |
| int hci_count = 0; |
| test_device()->SetTransactionCallback([&] { hci_count++; }); |
| set_random_address_change_allowed(false); |
| |
| // No HCI commands should be sent while disallowed. |
| addr_mgr()->EnablePrivacy(true); |
| RunUntilIdle(); |
| EXPECT_EQ(0, hci_count); |
| |
| EXPECT_TRUE(addr_mgr()->PrivacyEnabled()); |
| EXPECT_EQ(kPublic, EnsureLocalAddress()); |
| EXPECT_EQ(0, hci_count); |
| // Address hasn't changed so no notifications. |
| EXPECT_EQ(address_changed_cb_count(), 0u); |
| |
| // Requesting the address while address change is allowed should configure and |
| // return the new address. |
| set_random_address_change_allowed(true); |
| EXPECT_TRUE(EnsureLocalAddress().IsNonResolvablePrivate()); |
| EXPECT_EQ(1, hci_count); |
| // Address has changed. |
| EXPECT_EQ(address_changed_cb_count(), 1u); |
| } |
| |
| TEST_F(LowEnergyAddressManagerTest, AddressExpiration) { |
| const auto kSuccessResponse = |
| StaticByteBuffer(0x0E, |
| 4, // Command Complete, 4 bytes, |
| 1, // 1 allowed packet |
| 0x05, |
| 0x20, // opcode: HCI_LE_Set_Random_Address |
| 0x00 // status: success |
| ); |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), hci_spec::kLESetRandomAddress, &kSuccessResponse); |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), hci_spec::kLESetRandomAddress, &kSuccessResponse); |
| |
| addr_mgr()->EnablePrivacy(true); |
| auto addr1 = EnsureLocalAddress(); |
| EXPECT_TRUE(addr1.IsNonResolvablePrivate()); |
| // Address has changed. |
| EXPECT_EQ(address_changed_cb_count(), 1u); |
| |
| // Requesting the address again should keep returning the same address without |
| // sending any HCI commands. |
| int hci_count = 0; |
| test_device()->SetTransactionCallback([&] { hci_count++; }); |
| EXPECT_EQ(addr1, EnsureLocalAddress()); |
| EXPECT_EQ(addr1, EnsureLocalAddress()); |
| EXPECT_EQ(addr1, EnsureLocalAddress()); |
| EXPECT_EQ(addr1, EnsureLocalAddress()); |
| EXPECT_EQ(addr1, EnsureLocalAddress()); |
| EXPECT_EQ(0, hci_count); |
| // Address hasn't changed so no notifications. |
| EXPECT_EQ(address_changed_cb_count(), 1u); |
| |
| // A new address should be generated and configured after the random address |
| // interval. |
| RunFor(kPrivateAddressTimeout); |
| EXPECT_EQ(1, hci_count); |
| // Address has changed due to timeout. |
| EXPECT_EQ(address_changed_cb_count(), 2u); |
| |
| // Requesting the address again should return the new address. |
| auto addr2 = EnsureLocalAddress(); |
| EXPECT_TRUE(addr2.IsNonResolvablePrivate()); |
| EXPECT_NE(addr1, addr2); |
| EXPECT_EQ(1, hci_count); |
| // The new address was already reported after timeout so no other |
| // notification. |
| EXPECT_EQ(address_changed_cb_count(), 2u); |
| } |
| |
| TEST_F(LowEnergyAddressManagerTest, |
| AddressExpirationWhileAddressChangeIsDisallowed) { |
| const auto kSuccessResponse = |
| StaticByteBuffer(0x0E, |
| 4, // Command Complete, 4 bytes, |
| 1, // 1 allowed packet |
| 0x05, |
| 0x20, // opcode: HCI_LE_Set_Random_Address |
| 0x00 // status: success |
| ); |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), hci_spec::kLESetRandomAddress, &kSuccessResponse); |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), hci_spec::kLESetRandomAddress, &kSuccessResponse); |
| |
| addr_mgr()->EnablePrivacy(true); |
| auto addr1 = EnsureLocalAddress(); |
| EXPECT_TRUE(addr1.IsNonResolvablePrivate()); |
| |
| // Requesting the address again should keep returning the same address without |
| // sending any HCI commands. |
| int hci_count = 0; |
| test_device()->SetTransactionCallback([&] { hci_count++; }); |
| EXPECT_EQ(addr1, EnsureLocalAddress()); |
| EXPECT_EQ(addr1, EnsureLocalAddress()); |
| EXPECT_EQ(addr1, EnsureLocalAddress()); |
| EXPECT_EQ(addr1, EnsureLocalAddress()); |
| EXPECT_EQ(addr1, EnsureLocalAddress()); |
| EXPECT_EQ(0, hci_count); |
| |
| // After the interval ends, the address should be marked as expired but should |
| // not send an HCI command while the command is disallowed. |
| set_random_address_change_allowed(false); |
| RunFor(kPrivateAddressTimeout); |
| EXPECT_EQ(addr1, EnsureLocalAddress()); |
| EXPECT_EQ(0, hci_count); |
| |
| // Requesting the address again while the command is allowed should configure |
| // and return the new address. |
| set_random_address_change_allowed(true); |
| auto addr2 = EnsureLocalAddress(); |
| EXPECT_TRUE(addr2.IsNonResolvablePrivate()); |
| EXPECT_NE(addr1, addr2); |
| EXPECT_EQ(1, hci_count); |
| } |
| |
| TEST_F(LowEnergyAddressManagerTest, DisablePrivacy) { |
| // Enable privacy. |
| const auto kSuccessResponse = |
| StaticByteBuffer(0x0E, |
| 4, // Command Complete, 4 bytes, |
| 1, // 1 allowed packet |
| 0x05, |
| 0x20, // opcode: HCI_LE_Set_Random_Address |
| 0x00 // status: success |
| ); |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), hci_spec::kLESetRandomAddress, &kSuccessResponse); |
| |
| addr_mgr()->EnablePrivacy(true); |
| EXPECT_TRUE(EnsureLocalAddress().IsNonResolvablePrivate()); |
| // Address has changed to an NRPA. |
| EXPECT_EQ(address_changed_cb_count(), 1u); |
| |
| // Disable privacy. |
| addr_mgr()->EnablePrivacy(false); |
| |
| // The public address should be returned for the local address. |
| EXPECT_EQ(DeviceAddress::Type::kLEPublic, EnsureLocalAddress().type()); |
| // Address has change to public since Privacy is disabled. |
| EXPECT_EQ(address_changed_cb_count(), 2u); |
| |
| // No HCI commands should get sent after private address interval expires. |
| int hci_count = 0; |
| test_device()->SetTransactionCallback([&] { hci_count++; }); |
| RunFor(kPrivateAddressTimeout); |
| EXPECT_EQ(0, hci_count); |
| EXPECT_EQ(DeviceAddress::Type::kLEPublic, EnsureLocalAddress().type()); |
| } |
| |
| TEST_F(LowEnergyAddressManagerTest, DisablePrivacyDuringAddressChange) { |
| const auto kSuccessResponse = |
| StaticByteBuffer(0x0E, |
| 4, // Command Complete, 4 bytes, |
| 1, // 1 allowed packet |
| 0x05, |
| 0x20, // opcode: HCI_LE_Set_Random_Address |
| 0x00 // status: success |
| ); |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), hci_spec::kLESetRandomAddress, &kSuccessResponse); |
| |
| int hci_count = 0; |
| test_device()->SetTransactionCallback([&] { hci_count++; }); |
| |
| // Enable and disable in quick succession. HCI command should be sent but the |
| // local address shouldn't take effect. |
| addr_mgr()->EnablePrivacy(true); |
| addr_mgr()->EnablePrivacy(false); |
| EXPECT_EQ(DeviceAddress::Type::kLEPublic, EnsureLocalAddress().type()); |
| EXPECT_EQ(1, hci_count); |
| |
| // No HCI commands should get sent after private address interval expires. |
| RunFor(kPrivateAddressTimeout); |
| EXPECT_EQ(1, hci_count); |
| EXPECT_EQ(DeviceAddress::Type::kLEPublic, EnsureLocalAddress().type()); |
| } |
| |
| TEST_F(LowEnergyAddressManagerTest, |
| MultipleAddressChangedCallbacksAreNotified) { |
| // The |LowEnergyAddressManagerTest| registers an address changed callback on |
| // construction. Register another. |
| size_t cb_count2 = 0; |
| addr_mgr()->register_address_changed_callback([&](auto) { cb_count2++; }); |
| |
| // Enable privacy. |
| const auto kSuccessResponse = |
| StaticByteBuffer(0x0E, |
| 4, // Command Complete, 4 bytes, |
| 1, // 1 allowed packet |
| 0x05, |
| 0x20, // opcode: HCI_LE_Set_Random_Address |
| 0x00 // status: success |
| ); |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), hci_spec::kLESetRandomAddress, &kSuccessResponse); |
| |
| addr_mgr()->EnablePrivacy(true); |
| EXPECT_TRUE(EnsureLocalAddress().IsNonResolvablePrivate()); |
| // Address has changed to an NRPA. Both callbacks should be notified. |
| EXPECT_EQ(address_changed_cb_count(), 1u); |
| EXPECT_EQ(cb_count2, 1u); |
| |
| // Before privacy is disabled, another callback is registered. |
| size_t cb_count3 = 0; |
| addr_mgr()->register_address_changed_callback([&](auto) { cb_count3++; }); |
| EXPECT_EQ(cb_count3, 0u); |
| |
| // Disable privacy. |
| addr_mgr()->EnablePrivacy(false); |
| |
| // The public address should be returned for the local address. |
| EXPECT_EQ(DeviceAddress::Type::kLEPublic, EnsureLocalAddress().type()); |
| // Address has changed - all callbacks should be notified. |
| EXPECT_EQ(address_changed_cb_count(), 2u); |
| EXPECT_EQ(cb_count2, 2u); |
| EXPECT_EQ(cb_count3, 1u); |
| } |
| |
| } // namespace |
| } // namespace bt::gap |