blob: de43a3320583a0a2984d408ef25d822968594e7b [file] [log] [blame]
// 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