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