• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_address_manager.h"
16 
17 #include <pw_assert/check.h>
18 
19 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
20 #include "pw_bluetooth_sapphire/internal/host/gap/gap.h"
21 #include "pw_bluetooth_sapphire/internal/host/sm/util.h"
22 
23 namespace bt::gap {
24 
LowEnergyAddressManager(const DeviceAddress & public_address,StateQueryDelegate delegate,hci::CommandChannel::WeakPtr cmd_channel,pw::async::Dispatcher & dispatcher)25 LowEnergyAddressManager::LowEnergyAddressManager(
26     const DeviceAddress& public_address,
27     StateQueryDelegate delegate,
28     hci::CommandChannel::WeakPtr cmd_channel,
29     pw::async::Dispatcher& dispatcher)
30     : dispatcher_(dispatcher),
31       delegate_(std::move(delegate)),
32       cmd_(std::move(cmd_channel)),
33       privacy_enabled_(false),
34       public_(public_address),
35       needs_refresh_(false),
36       refreshing_(false),
37       weak_self_(this) {
38   PW_DCHECK(public_.type() == DeviceAddress::Type::kLEPublic);
39   PW_DCHECK(delegate_);
40   PW_DCHECK(cmd_.is_alive());
41 }
42 
~LowEnergyAddressManager()43 LowEnergyAddressManager::~LowEnergyAddressManager() { CancelExpiry(); }
44 
EnablePrivacy(bool enabled)45 void LowEnergyAddressManager::EnablePrivacy(bool enabled) {
46   if (enabled == privacy_enabled_) {
47     bt_log(DEBUG,
48            "gap-le",
49            "privacy already %s",
50            (enabled ? "enabled" : "disabled"));
51     return;
52   }
53 
54   privacy_enabled_ = enabled;
55 
56   if (!enabled) {
57     CleanUpPrivacyState();
58     ResolveAddressRequests();
59     NotifyAddressUpdate();
60     return;
61   }
62 
63   needs_refresh_ = true;
64 
65   TryRefreshRandomAddress();
66 }
67 
EnsureLocalAddress(std::optional<DeviceAddress::Type> address_type,AddressCallback callback)68 void LowEnergyAddressManager::EnsureLocalAddress(
69     std::optional<DeviceAddress::Type> address_type, AddressCallback callback) {
70   PW_DCHECK(callback);
71 
72   if (!privacy_enabled_ && address_type.has_value() &&
73       address_type.value() == DeviceAddress::Type::kLERandom) {
74     bt_log(WARN,
75            "hci-le",
76            "Cannot advertise a random address while privacy is disabled");
77     callback(fit::error(HostError::kInvalidParameters));
78     return;
79   }
80 
81   if (address_type == DeviceAddress::Type::kLEPublic || !privacy_enabled_) {
82     callback(fit::ok(public_address()));
83     return;
84   }
85 
86   // Report the address right away if it doesn't need refreshing.
87   if (!needs_refresh_) {
88     callback(fit::ok(current_address()));
89     return;
90   }
91 
92   address_callbacks_.push(std::move(callback));
93   TryRefreshRandomAddress();
94 }
95 
TryRefreshRandomAddress()96 void LowEnergyAddressManager::TryRefreshRandomAddress() {
97   if (!privacy_enabled_ || !needs_refresh_) {
98     bt_log(DEBUG, "gap-le", "address does not need refresh");
99     return;
100   }
101 
102   if (refreshing_) {
103     bt_log(DEBUG, "gap-le", "address update in progress");
104     return;
105   }
106 
107   if (!CanUpdateRandomAddress()) {
108     bt_log(DEBUG,
109            "gap-le",
110            "deferring local address refresh due to ongoing procedures");
111     // Don't stall procedures that requested the current address while in this
112     // state.
113     ResolveAddressRequests();
114     return;
115   }
116 
117   CancelExpiry();
118   refreshing_ = true;
119 
120   DeviceAddress random_addr;
121   if (irk_) {
122     random_addr = sm::util::GenerateRpa(*irk_);
123   } else {
124     random_addr = sm::util::GenerateRandomAddress(/*is_static=*/false);
125   }
126 
127   auto cmd = hci::CommandPacket::New<
128       pw::bluetooth::emboss::LESetRandomAddressCommandWriter>(
129       hci_spec::kLESetRandomAddress);
130   cmd.view_t().random_address().CopyFrom(random_addr.value().view());
131 
132   auto self = weak_self_.GetWeakPtr();
133   auto cmd_complete_cb = [self, this, random_addr](
134                              auto, const hci::EventPacket& event) {
135     if (!self.is_alive()) {
136       return;
137     }
138 
139     refreshing_ = false;
140 
141     if (!privacy_enabled_) {
142       bt_log(DEBUG,
143              "gap-le",
144              "ignore random address result while privacy is disabled");
145       return;
146     }
147 
148     if (!HCI_IS_ERROR(
149             event, TRACE, "gap-le", "failed to update random address")) {
150       needs_refresh_ = false;
151       random_ = random_addr;
152       bt_log(INFO, "gap-le", "random address updated: %s", bt_str(*random_));
153 
154       // Set the new random address to expire in kPrivateAddressTimeout.
155       random_address_expiry_task_.set_function(
156           [this](pw::async::Context /*ctx*/, pw::Status status) {
157             if (status.ok()) {
158               needs_refresh_ = true;
159               TryRefreshRandomAddress();
160             }
161           });
162       random_address_expiry_task_.PostAfter(kPrivateAddressTimeout);
163 
164       // Notify any listeners of the change in device address.
165       NotifyAddressUpdate();
166     }
167 
168     ResolveAddressRequests();
169   };
170 
171   cmd_->SendCommand(std::move(cmd), std::move(cmd_complete_cb));
172 }
173 
CleanUpPrivacyState()174 void LowEnergyAddressManager::CleanUpPrivacyState() {
175   privacy_enabled_ = false;
176   needs_refresh_ = false;
177   CancelExpiry();
178 }
179 
CancelExpiry()180 void LowEnergyAddressManager::CancelExpiry() {
181   random_address_expiry_task_.Cancel();
182 }
183 
CanUpdateRandomAddress() const184 bool LowEnergyAddressManager::CanUpdateRandomAddress() const {
185   PW_DCHECK(delegate_);
186   return delegate_();
187 }
188 
ResolveAddressRequests()189 void LowEnergyAddressManager::ResolveAddressRequests() {
190   auto address = current_address();
191   auto q = std::move(address_callbacks_);
192   bt_log(DEBUG, "gap-le", "using local address %s", address.ToString().c_str());
193   while (!q.empty()) {
194     q.front()(fit::ok(address));
195     q.pop();
196   }
197 }
198 
NotifyAddressUpdate()199 void LowEnergyAddressManager::NotifyAddressUpdate() {
200   auto address = current_address();
201   for (auto& cb : address_changed_callbacks_) {
202     cb(fit::ok(address));
203   }
204 }
205 
206 }  // namespace bt::gap
207