| // 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/sm/util.h" |
| #include "src/connectivity/bluetooth/core/bt-host/transport/transport.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(/*is_static=*/false); |
| } |
| |
| auto cmd = hci::CommandPacket::New(hci_spec::kLESetRandomAddress, |
| sizeof(hci_spec::LESetRandomAddressCommandParams)); |
| auto params = cmd->mutable_payload<hci_spec::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 |