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 BT_DEBUG_ASSERT(public_.type() == DeviceAddress::Type::kLEPublic);
37 BT_DEBUG_ASSERT(delegate_);
38 BT_DEBUG_ASSERT(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(AddressCallback callback)66 void LowEnergyAddressManager::EnsureLocalAddress(AddressCallback callback) {
67 BT_DEBUG_ASSERT(callback);
68
69 // Report the address right away if it doesn't need refreshing.
70 if (!needs_refresh_) {
71 callback(current_address());
72 return;
73 }
74
75 address_callbacks_.push(std::move(callback));
76 TryRefreshRandomAddress();
77 }
78
TryRefreshRandomAddress()79 void LowEnergyAddressManager::TryRefreshRandomAddress() {
80 if (!privacy_enabled_ || !needs_refresh_) {
81 bt_log(DEBUG, "gap-le", "address does not need refresh");
82 return;
83 }
84
85 if (refreshing_) {
86 bt_log(DEBUG, "gap-le", "address update in progress");
87 return;
88 }
89
90 if (!CanUpdateRandomAddress()) {
91 bt_log(DEBUG,
92 "gap-le",
93 "deferring local address refresh due to ongoing procedures");
94 // Don't stall procedures that requested the current address while in this
95 // state.
96 ResolveAddressRequests();
97 return;
98 }
99
100 CancelExpiry();
101 refreshing_ = true;
102
103 DeviceAddress random_addr;
104 if (irk_) {
105 random_addr = sm::util::GenerateRpa(*irk_);
106 } else {
107 random_addr = sm::util::GenerateRandomAddress(/*is_static=*/false);
108 }
109
110 auto cmd = hci::EmbossCommandPacket::New<
111 pw::bluetooth::emboss::LESetRandomAddressCommandWriter>(
112 hci_spec::kLESetRandomAddress);
113 cmd.view_t().random_address().CopyFrom(random_addr.value().view());
114
115 auto self = weak_self_.GetWeakPtr();
116 auto cmd_complete_cb = [self, this, random_addr](
117 auto id, const hci::EventPacket& event) {
118 if (!self.is_alive()) {
119 return;
120 }
121
122 refreshing_ = false;
123
124 if (!privacy_enabled_) {
125 bt_log(DEBUG,
126 "gap-le",
127 "ignore random address result while privacy is disabled");
128 return;
129 }
130
131 if (!hci_is_error(
132 event, TRACE, "gap-le", "failed to update random address")) {
133 needs_refresh_ = false;
134 random_ = random_addr;
135 bt_log(INFO, "gap-le", "random address updated: %s", bt_str(*random_));
136
137 // Set the new random address to expire in kPrivateAddressTimeout.
138 random_address_expiry_task_.set_function(
139 [this](pw::async::Context /*ctx*/, pw::Status status) {
140 if (status.ok()) {
141 needs_refresh_ = true;
142 TryRefreshRandomAddress();
143 }
144 });
145 random_address_expiry_task_.PostAfter(kPrivateAddressTimeout);
146
147 // Notify any listeners of the change in device address.
148 NotifyAddressUpdate();
149 }
150
151 ResolveAddressRequests();
152 };
153
154 cmd_->SendCommand(std::move(cmd), std::move(cmd_complete_cb));
155 }
156
CleanUpPrivacyState()157 void LowEnergyAddressManager::CleanUpPrivacyState() {
158 privacy_enabled_ = false;
159 needs_refresh_ = false;
160 CancelExpiry();
161 }
162
CancelExpiry()163 void LowEnergyAddressManager::CancelExpiry() {
164 random_address_expiry_task_.Cancel();
165 }
166
CanUpdateRandomAddress() const167 bool LowEnergyAddressManager::CanUpdateRandomAddress() const {
168 BT_DEBUG_ASSERT(delegate_);
169 return delegate_();
170 }
171
ResolveAddressRequests()172 void LowEnergyAddressManager::ResolveAddressRequests() {
173 auto address = current_address();
174 auto q = std::move(address_callbacks_);
175 bt_log(DEBUG, "gap-le", "using local address %s", address.ToString().c_str());
176 while (!q.empty()) {
177 q.front()(address);
178 q.pop();
179 }
180 }
181
NotifyAddressUpdate()182 void LowEnergyAddressManager::NotifyAddressUpdate() {
183 auto address = current_address();
184 for (auto& cb : address_changed_callbacks_) {
185 cb(address);
186 }
187 }
188
189 } // namespace bt::gap
190