blob: 68c0a7b987975e584d12d358c26a3516b072b1f6 [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 "low_energy_address_manager.h"
#include <lib/async/default.h>
#include "gap.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/transport.h"
#include "src/connectivity/bluetooth/core/bt-host/sm/util.h"
namespace bt::gap {
LowEnergyAddressManager::LowEnergyAddressManager(const DeviceAddress& public_address,
StateQueryDelegate delegate,
fxl::WeakPtr<hci::Transport> hci)
: delegate_(std::move(delegate)),
hci_(std::move(hci)),
privacy_enabled_(false),
public_(public_address),
needs_refresh_(false),
refreshing_(false),
weak_ptr_factory_(this) {
ZX_DEBUG_ASSERT(public_.type() == DeviceAddress::Type::kLEPublic);
ZX_DEBUG_ASSERT(delegate_);
ZX_DEBUG_ASSERT(hci_);
}
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();
return;
}
needs_refresh_ = true;
TryRefreshRandomAddress();
}
void LowEnergyAddressManager::EnsureLocalAddress(AddressCallback callback) {
ZX_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(false /* is_static */);
}
auto cmd = hci::CommandPacket::New(hci::kLESetRandomAddress,
sizeof(hci::LESetRandomAddressCommandParams));
auto params = cmd->mutable_payload<hci::LESetRandomAddressCommandParams>();
params->random_address = random_addr.value();
auto self = weak_ptr_factory_.GetWeakPtr();
auto cmd_complete_cb = [self, this, random_addr](auto id, const hci::EventPacket& event) {
if (!self) {
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_handler([this](auto*, auto*, zx_status_t status) {
if (status == ZX_OK) {
needs_refresh_ = true;
TryRefreshRandomAddress();
}
});
random_address_expiry_task_.PostDelayed(async_get_default_dispatcher(),
kPrivateAddressTimeout);
}
ResolveAddressRequests();
};
hci_->command_channel()->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 {
ZX_DEBUG_ASSERT(delegate_);
return delegate_();
}
void LowEnergyAddressManager::ResolveAddressRequests() {
auto q = std::move(address_callbacks_);
bt_log(DEBUG, "gap-le", "using local address %s", current_address().ToString().c_str());
while (!q.empty()) {
q.front()(current_address());
q.pop();
}
}
} // namespace bt::gap