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