blob: db5109a5d40c080a8e14109f03f0a9da1d378a43 [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/common/log.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"
namespace bt::gap {
LowEnergyAddressManager::LowEnergyAddressManager(
const DeviceAddress& public_address,
StateQueryDelegate delegate,
hci::CommandChannel::WeakPtr cmd_channel,
pw::async::Dispatcher& dispatcher)
: dispatcher_(dispatcher),
delegate_(std::move(delegate)),
cmd_(std::move(cmd_channel)),
privacy_enabled_(false),
public_(public_address),
needs_refresh_(false),
refreshing_(false),
weak_self_(this) {
BT_DEBUG_ASSERT(public_.type() == DeviceAddress::Type::kLEPublic);
BT_DEBUG_ASSERT(delegate_);
BT_DEBUG_ASSERT(cmd_.is_alive());
}
LowEnergyAddressManager::~LowEnergyAddressManager() { CancelExpiry(); }
void LowEnergyAddressManager::EnablePrivacy(bool enabled) {
if (enabled == privacy_enabled_) {
bt_log(DEBUG,
"gap-le",
"privacy already %s",
(enabled ? "enabled" : "disabled"));
return;
}
privacy_enabled_ = enabled;
if (!enabled) {
CleanUpPrivacyState();
ResolveAddressRequests();
NotifyAddressUpdate();
return;
}
needs_refresh_ = true;
TryRefreshRandomAddress();
}
void LowEnergyAddressManager::EnsureLocalAddress(AddressCallback callback) {
BT_DEBUG_ASSERT(callback);
// Report the address right away if it doesn't need refreshing.
if (!needs_refresh_) {
callback(current_address());
return;
}
address_callbacks_.push(std::move(callback));
TryRefreshRandomAddress();
}
void LowEnergyAddressManager::TryRefreshRandomAddress() {
if (!privacy_enabled_ || !needs_refresh_) {
bt_log(DEBUG, "gap-le", "address does not need refresh");
return;
}
if (refreshing_) {
bt_log(DEBUG, "gap-le", "address update in progress");
return;
}
if (!CanUpdateRandomAddress()) {
bt_log(DEBUG,
"gap-le",
"deferring local address refresh due to ongoing procedures");
// Don't stall procedures that requested the current address while in this
// state.
ResolveAddressRequests();
return;
}
CancelExpiry();
refreshing_ = true;
DeviceAddress random_addr;
if (irk_) {
random_addr = sm::util::GenerateRpa(*irk_);
} else {
random_addr = sm::util::GenerateRandomAddress(/*is_static=*/false);
}
auto cmd = hci::EmbossCommandPacket::New<
pw::bluetooth::emboss::LESetRandomAddressCommandWriter>(
hci_spec::kLESetRandomAddress);
cmd.view_t().random_address().CopyFrom(random_addr.value().view());
auto self = weak_self_.GetWeakPtr();
auto cmd_complete_cb = [self, this, random_addr](
auto id, const hci::EventPacket& event) {
if (!self.is_alive()) {
return;
}
refreshing_ = false;
if (!privacy_enabled_) {
bt_log(DEBUG,
"gap-le",
"ignore random address result while privacy is disabled");
return;
}
if (!hci_is_error(
event, TRACE, "gap-le", "failed to update random address")) {
needs_refresh_ = false;
random_ = random_addr;
bt_log(INFO, "gap-le", "random address updated: %s", bt_str(*random_));
// Set the new random address to expire in kPrivateAddressTimeout.
random_address_expiry_task_.set_function(
[this](pw::async::Context /*ctx*/, pw::Status status) {
if (status.ok()) {
needs_refresh_ = true;
TryRefreshRandomAddress();
}
});
random_address_expiry_task_.PostAfter(kPrivateAddressTimeout);
// Notify any listeners of the change in device address.
NotifyAddressUpdate();
}
ResolveAddressRequests();
};
cmd_->SendCommand(std::move(cmd), std::move(cmd_complete_cb));
}
void LowEnergyAddressManager::CleanUpPrivacyState() {
privacy_enabled_ = false;
needs_refresh_ = false;
CancelExpiry();
}
void LowEnergyAddressManager::CancelExpiry() {
random_address_expiry_task_.Cancel();
}
bool LowEnergyAddressManager::CanUpdateRandomAddress() const {
BT_DEBUG_ASSERT(delegate_);
return delegate_();
}
void LowEnergyAddressManager::ResolveAddressRequests() {
auto address = current_address();
auto q = std::move(address_callbacks_);
bt_log(DEBUG, "gap-le", "using local address %s", address.ToString().c_str());
while (!q.empty()) {
q.front()(address);
q.pop();
}
}
void LowEnergyAddressManager::NotifyAddressUpdate() {
auto address = current_address();
for (auto& cb : address_changed_callbacks_) {
cb(address);
}
}
} // namespace bt::gap