xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/gap/low_energy_address_manager.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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