• 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/peer.h"
16 
17 #include <cpp-string/string_printf.h>
18 #include <pw_assert/check.h>
19 #include <pw_bytes/endian.h>
20 #include <pw_string/utf_codecs.h>
21 
22 #include <cinttypes>
23 
24 #include "pw_bluetooth_sapphire/internal/host/common/advertising_data.h"
25 #include "pw_bluetooth_sapphire/internal/host/common/manufacturer_names.h"
26 #include "pw_bluetooth_sapphire/internal/host/common/uuid.h"
27 #include "pw_bluetooth_sapphire/internal/host/gap/gap.h"
28 #include "pw_bluetooth_sapphire/internal/host/hci-spec/util.h"
29 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
30 
31 namespace bt::gap {
32 namespace {
33 // To prevent log spam, we only log every Nth failure to parse AdvertisingData
34 // from each peer. This value controls N.
35 const int64_t kAdvDataParseFailureWarnLogInterval = 25;
36 }  // namespace
37 
ConnectionStateToString(Peer::ConnectionState state)38 std::string Peer::ConnectionStateToString(Peer::ConnectionState state) {
39   switch (state) {
40     case Peer::ConnectionState::kNotConnected:
41       return "not connected";
42     case Peer::ConnectionState::kInitializing:
43       return "connecting";
44     case Peer::ConnectionState::kConnected:
45       return "connected";
46   }
47 
48   PW_CRASH("invalid connection state %u", static_cast<unsigned int>(state));
49   return "(unknown)";
50 }
51 
NameSourceToString(Peer::NameSource name_source)52 std::string Peer::NameSourceToString(Peer::NameSource name_source) {
53   switch (name_source) {
54     case Peer::NameSource::kNameDiscoveryProcedure:
55       return "Name Discovery Procedure";
56     case Peer::NameSource::kAdvertisingDataComplete:
57       return "Advertising data (complete)";
58     case Peer::NameSource::kAdvertisingDataShortened:
59       return "Advertising data (shortened)";
60     case Peer::NameSource::kInquiryResultComplete:
61       return "Inquiry result (complete)";
62     case Peer::NameSource::kInquiryResultShortened:
63       return "Inquiry result (shortened)";
64     case Peer::NameSource::kGenericAccessService:
65       return "Generic Access Service";
66     case Peer::NameSource::kUnknown:
67       return "Unknown source";
68   }
69 
70   PW_CRASH("invalid peer name source %u",
71            static_cast<unsigned int>(name_source));
72   return "(unknown)";
73 }
74 
LowEnergyData(Peer * owner)75 Peer::LowEnergyData::LowEnergyData(Peer* owner)
76     : peer_(owner),
77       bond_data_(std::nullopt,
78                  [](const std::optional<sm::PairingData>& p) {
79                    return p.has_value();
80                  }),
81       auto_conn_behavior_(AutoConnectBehavior::kAlways),
82       features_(std::nullopt,
__anonab3fe1f50302(const std::optional<hci_spec::LESupportedFeatures> f) 83                 [](const std::optional<hci_spec::LESupportedFeatures> f) {
84                   return f ? bt_lib_cpp_string::StringPrintf("%#.16" PRIx64, *f)
85                            : "";
86                 }),
87       service_changed_gatt_data_({.notify = false, .indicate = false}) {
88   PW_DCHECK(peer_);
89 }
90 
AttachInspect(inspect::Node & parent,std::string name)91 void Peer::LowEnergyData::AttachInspect(inspect::Node& parent,
92                                         std::string name) {
93   node_ = parent.CreateChild(name);
94   inspect_properties_.connection_state =
95       node_.CreateString(LowEnergyData::kInspectConnectionStateName,
96                          Peer::ConnectionStateToString(connection_state()));
97   inspect_properties_.last_adv_data_parse_failure = node_.CreateString(
98       LowEnergyData::kInspectLastAdvertisingDataParseFailureName, "");
99   adv_data_parse_failure_count_.AttachInspect(
100       node_, LowEnergyData::kInspectAdvertisingDataParseFailureCountName);
101   bond_data_.AttachInspect(node_, LowEnergyData::kInspectBondDataName);
102   features_.AttachInspect(node_, LowEnergyData::kInspectFeaturesName);
103 }
104 
SetAdvertisingData(int8_t rssi,const ByteBuffer & data,pw::chrono::SystemClock::time_point timestamp)105 void Peer::LowEnergyData::SetAdvertisingData(
106     int8_t rssi,
107     const ByteBuffer& data,
108     pw::chrono::SystemClock::time_point timestamp) {
109   // Prolong this peer's expiration in case it is temporary.
110   peer_->UpdateExpiry();
111 
112   peer_->SetRssiInternal(rssi);
113 
114   AdvertisingData::ParseResult res = AdvertisingData::FromBytes(data);
115   if (!res.is_ok()) {
116     int64_t current_failure_count = *adv_data_parse_failure_count_;
117     adv_data_parse_failure_count_.Set(current_failure_count + 1);
118     inspect_properties_.last_adv_data_parse_failure.Set(
119         AdvertisingData::ParseErrorToString(res.error_value()));
120     std::string message = bt_lib_cpp_string::StringPrintf(
121         "failed to parse advertising data: %s (peer: %s)",
122         bt::AdvertisingData::ParseErrorToString(res.error_value()).c_str(),
123         bt_str(peer_->identifier()));
124     // To prevent log spam, we only log the first, and then every Nth failure to
125     // parse AdvertisingData from each peer at WARN level. Other failures are
126     // logged at DEBUG level.
127     if (*adv_data_parse_failure_count_ % kAdvDataParseFailureWarnLogInterval ==
128         1) {
129       bt_log(WARN, "gap-le", "%s", message.c_str());
130     } else {
131       bt_log(DEBUG, "gap-le", "%s", message.c_str());
132     }
133     // Update the error if we don't have a successful parse already
134     if (parsed_adv_data_.is_error()) {
135       parsed_adv_data_ = std::move(res);
136     }
137   } else {
138     // Update the advertising data
139     adv_data_buffer_ = DynamicByteBuffer(data.size());
140     data.Copy(&adv_data_buffer_);
141     parsed_adv_data_ = std::move(res);
142     // Only update the parsed_adv_timestamp if the AdvertisingData parsed
143     // successfully
144     parsed_adv_timestamp_ = timestamp;
145 
146     // Do not update the name of bonded peers because advertisements are
147     // unauthenticated.
148     // TODO(fxbug.dev/42166256): Populate more Peer fields with relevant fields
149     // from parsed_adv_data_.
150     if (!peer_->bonded() && parsed_adv_data_->local_name().has_value()) {
151       peer_->RegisterNameInternal(
152           parsed_adv_data_->local_name()->name,
153           parsed_adv_data_->local_name()->is_complete
154               ? Peer::NameSource::kAdvertisingDataComplete
155               : Peer::NameSource::kAdvertisingDataShortened);
156     }
157   }
158 
159   peer_->UpdatePeerAndNotifyListeners(NotifyListenersChange::kBondNotUpdated);
160 }
161 
162 Peer::InitializingConnectionToken
RegisterInitializingConnection()163 Peer::LowEnergyData::RegisterInitializingConnection() {
164   ConnectionState prev_state = connection_state();
165   initializing_tokens_count_++;
166   OnConnectionStateMaybeChanged(prev_state);
167 
168   auto unregister_cb = [self = peer_->GetWeakPtr(), this] {
169     if (!self.is_alive()) {
170       return;
171     }
172 
173     ConnectionState conn_prev_state = connection_state();
174     initializing_tokens_count_--;
175     OnConnectionStateMaybeChanged(conn_prev_state);
176   };
177 
178   return InitializingConnectionToken(std::move(unregister_cb));
179 }
180 
RegisterConnection()181 Peer::ConnectionToken Peer::LowEnergyData::RegisterConnection() {
182   // The high-level connection state is the same whether one or many registered
183   // connections exist, but we track each connection in metrics to support
184   // multiple connections to the same peer.
185   peer_->peer_metrics_->LogLeConnection();
186 
187   ConnectionState prev_state = connection_state();
188   connection_tokens_count_++;
189   OnConnectionStateMaybeChanged(prev_state);
190 
191   auto unregister_cb = [self = peer_->GetWeakPtr(), this] {
192     if (!self.is_alive()) {
193       return;
194     }
195 
196     connection_tokens_count_--;
197     peer_->peer_metrics_->LogLeDisconnection();
198     OnConnectionStateMaybeChanged(/*previous=*/ConnectionState::kConnected);
199   };
200 
201   return ConnectionToken(std::move(unregister_cb));
202 }
203 
RegisterPairing()204 Peer::PairingToken Peer::LowEnergyData::RegisterPairing() {
205   pairing_tokens_count_++;
206   auto unregister_cb = [self = peer_->GetWeakPtr(), this] {
207     if (!self.is_alive()) {
208       return;
209     }
210     pairing_tokens_count_--;
211     OnPairingMaybeComplete();
212   };
213   return PairingToken(std::move(unregister_cb));
214 }
215 
is_pairing() const216 bool Peer::LowEnergyData::is_pairing() const {
217   return pairing_tokens_count_ > 0;
218 }
219 
add_pairing_completion_callback(fit::callback<void ()> && callback)220 void Peer::LowEnergyData::add_pairing_completion_callback(
221     fit::callback<void()>&& callback) {
222   pairing_complete_callbacks_.emplace_back(std::move(callback));
223   OnPairingMaybeComplete();
224 }
225 
OnPairingMaybeComplete()226 void Peer::LowEnergyData::OnPairingMaybeComplete() {
227   if (pairing_tokens_count_ > 0 || pairing_complete_callbacks_.empty()) {
228     return;
229   }
230   std::vector<fit::callback<void()>> callbacks;
231   std::swap(callbacks, pairing_complete_callbacks_);
232   for (auto& cb : callbacks) {
233     cb();
234   }
235 }
236 
SetConnectionParameters(const hci_spec::LEConnectionParameters & params)237 void Peer::LowEnergyData::SetConnectionParameters(
238     const hci_spec::LEConnectionParameters& params) {
239   PW_DCHECK(peer_->connectable());
240   conn_params_ = params;
241 }
242 
SetPreferredConnectionParameters(const hci_spec::LEPreferredConnectionParameters & params)243 void Peer::LowEnergyData::SetPreferredConnectionParameters(
244     const hci_spec::LEPreferredConnectionParameters& params) {
245   PW_DCHECK(peer_->connectable());
246   preferred_conn_params_ = params;
247 }
248 
StoreBond(const sm::PairingData & bond_data)249 bool Peer::LowEnergyData::StoreBond(const sm::PairingData& bond_data) {
250   return peer_->store_le_bond_callback_(bond_data);
251 }
252 
SetBondData(const sm::PairingData & bond_data)253 void Peer::LowEnergyData::SetBondData(const sm::PairingData& bond_data) {
254   PW_DCHECK(peer_->connectable());
255   PW_DCHECK(peer_->address().type() != DeviceAddress::Type::kLEAnonymous);
256 
257   // TODO(fxbug.dev/42072204): Do not overwrite an existing key that has
258   // greater strength or authentication.
259 
260   // Make sure the peer is non-temporary.
261   peer_->TryMakeNonTemporary();
262 
263   // This will mark the peer as bonded
264   bond_data_.Set(bond_data);
265 
266   // Update to the new identity address if the current address is random.
267   if (peer_->address().type() == DeviceAddress::Type::kLERandom &&
268       bond_data.identity_address) {
269     peer_->set_identity_known(true);
270     peer_->set_address(*bond_data.identity_address);
271   }
272 
273   // PeerCache notifies listeners of new bonds, so no need to request that here.
274   peer_->UpdatePeerAndNotifyListeners(NotifyListenersChange::kBondNotUpdated);
275 }
276 
ClearBondData()277 void Peer::LowEnergyData::ClearBondData() {
278   PW_CHECK(bond_data_->has_value());
279   if (bond_data_->value().irk) {
280     peer_->set_identity_known(false);
281   }
282   bond_data_.Set(std::nullopt);
283 }
284 
OnConnectionStateMaybeChanged(ConnectionState previous)285 void Peer::LowEnergyData::OnConnectionStateMaybeChanged(
286     ConnectionState previous) {
287   if (connection_state() == previous) {
288     return;
289   }
290 
291   bt_log(DEBUG,
292          "gap-le",
293          "peer (%s) LE connection state changed from %s to %s (initializing "
294          "count: %hu, "
295          "connection count: %hu)",
296          bt_str(peer_->identifier()),
297          ConnectionStateToString(previous).c_str(),
298          ConnectionStateToString(connection_state()).c_str(),
299          initializing_tokens_count_,
300          connection_tokens_count_);
301 
302   inspect_properties_.connection_state.Set(
303       ConnectionStateToString(connection_state()));
304 
305   if (previous == ConnectionState::kNotConnected) {
306     peer_->TryMakeNonTemporary();
307   } else if (connection_state() == ConnectionState::kNotConnected) {
308     peer_->TryMakeTemporary();
309   }
310 
311   peer_->UpdateExpiry();
312   peer_->UpdatePeerAndNotifyListeners(NotifyListenersChange::kBondNotUpdated);
313 }
314 
BrEdrData(Peer * owner)315 Peer::BrEdrData::BrEdrData(Peer* owner)
316     : peer_(owner), services_({}, MakeContainerOfToStringConvertFunction()) {
317   PW_DCHECK(peer_);
318   PW_DCHECK(peer_->identity_known());
319 
320   // Devices that are capable of BR/EDR and use a LE random device address will
321   // end up with separate entries for the BR/EDR and LE addresses.
322   PW_DCHECK(peer_->address().type() != DeviceAddress::Type::kLERandom &&
323             peer_->address().type() != DeviceAddress::Type::kLEAnonymous);
324   address_ = {DeviceAddress::Type::kBREDR, peer_->address().value()};
325 }
326 
AttachInspect(inspect::Node & parent,std::string name)327 void Peer::BrEdrData::AttachInspect(inspect::Node& parent, std::string name) {
328   node_ = parent.CreateChild(name);
329   inspect_properties_.connection_state =
330       node_.CreateString(BrEdrData::kInspectConnectionStateName,
331                          ConnectionStateToString(connection_state()));
332 
333   if (bonded()) {
334     link_key_.value().AttachInspect(node_, BrEdrData::kInspectLinkKeyName);
335   }
336   services_.AttachInspect(node_, BrEdrData::kInspectServicesName);
337 }
338 
SetInquiryData(const pw::bluetooth::emboss::InquiryResultView & view)339 void Peer::BrEdrData::SetInquiryData(
340     const pw::bluetooth::emboss::InquiryResultView& view) {
341   PW_DCHECK(peer_->address().value() == DeviceAddressBytes{view.bd_addr()});
342   SetInquiryData(
343       DeviceClass(view.class_of_device().BackingStorage().ReadUInt()),
344       view.clock_offset().BackingStorage().ReadUInt(),
345       view.page_scan_repetition_mode().Read());
346 }
347 
SetInquiryData(const pw::bluetooth::emboss::InquiryResultWithRssiView & view)348 void Peer::BrEdrData::SetInquiryData(
349     const pw::bluetooth::emboss::InquiryResultWithRssiView& view) {
350   PW_DCHECK(peer_->address().value() == DeviceAddressBytes{view.bd_addr()});
351   SetInquiryData(
352       DeviceClass(view.class_of_device().BackingStorage().ReadUInt()),
353       view.clock_offset().BackingStorage().ReadUInt(),
354       view.page_scan_repetition_mode().Read(),
355       view.rssi().Read());
356 }
357 
SetInquiryData(const pw::bluetooth::emboss::ExtendedInquiryResultEventView & view)358 void Peer::BrEdrData::SetInquiryData(
359     const pw::bluetooth::emboss::ExtendedInquiryResultEventView& view) {
360   PW_DCHECK(peer_->address().value() == DeviceAddressBytes(view.bd_addr()));
361   const BufferView response_view(
362       view.extended_inquiry_response().BackingStorage().data(),
363       view.extended_inquiry_response().SizeInBytes());
364   SetInquiryData(
365       DeviceClass(view.class_of_device().BackingStorage().ReadUInt()),
366       view.clock_offset().BackingStorage().ReadUInt(),
367       view.page_scan_repetition_mode().Read(),
368       view.rssi().Read(),
369       response_view);
370 }
371 
SetIncomingRequest(const pw::bluetooth::emboss::ConnectionRequestEventView & view)372 void Peer::BrEdrData::SetIncomingRequest(
373     const pw::bluetooth::emboss::ConnectionRequestEventView& view) {
374   device_class_ =
375       DeviceClass(view.class_of_device().BackingStorage().ReadUInt());
376 }
377 
378 Peer::InitializingConnectionToken
RegisterInitializingConnection()379 Peer::BrEdrData::RegisterInitializingConnection() {
380   PW_CHECK(!connected());
381 
382   ConnectionState prev_state = connection_state();
383   initializing_tokens_count_++;
384   OnConnectionStateMaybeChanged(prev_state);
385 
386   return InitializingConnectionToken([self = peer_->GetWeakPtr(), this] {
387     if (!self.is_alive()) {
388       return;
389     }
390 
391     ConnectionState conn_prev_state = connection_state();
392     initializing_tokens_count_--;
393     OnConnectionStateMaybeChanged(conn_prev_state);
394   });
395 }
396 
RegisterConnection()397 Peer::ConnectionToken Peer::BrEdrData::RegisterConnection() {
398   PW_CHECK(!connected(),
399            "attempt to register BR/EDR connection when a connection is "
400            "already registered (peer: %s)",
401            bt_str(peer_->identifier()));
402 
403   ConnectionState prev_state = connection_state();
404   connection_tokens_count_++;
405   OnConnectionStateMaybeChanged(prev_state);
406 
407   return ConnectionToken([self = peer_->GetWeakPtr(), this] {
408     if (!self.is_alive()) {
409       return;
410     }
411 
412     ConnectionState conn_prev_state = connection_state();
413     connection_tokens_count_--;
414     OnConnectionStateMaybeChanged(conn_prev_state);
415   });
416 }
417 
RegisterPairing()418 Peer::PairingToken Peer::BrEdrData::RegisterPairing() {
419   pairing_tokens_count_++;
420   auto unregister_cb = [self = peer_->GetWeakPtr(), this] {
421     if (!self.is_alive()) {
422       return;
423     }
424     pairing_tokens_count_--;
425     OnPairingMaybeComplete();
426   };
427   return PairingToken(std::move(unregister_cb));
428 }
429 
is_pairing() const430 bool Peer::BrEdrData::is_pairing() const { return pairing_tokens_count_ > 0; }
431 
add_pairing_completion_callback(fit::callback<void ()> && callback)432 void Peer::BrEdrData::add_pairing_completion_callback(
433     fit::callback<void()>&& callback) {
434   pairing_complete_callbacks_.emplace_back(std::move(callback));
435   OnPairingMaybeComplete();
436 }
437 
OnPairingMaybeComplete()438 void Peer::BrEdrData::OnPairingMaybeComplete() {
439   if (pairing_tokens_count_ > 0 || pairing_complete_callbacks_.empty()) {
440     return;
441   }
442   std::vector<fit::callback<void()>> callbacks;
443   std::swap(callbacks, pairing_complete_callbacks_);
444   for (auto& cb : callbacks) {
445     cb();
446   }
447 }
448 
OnConnectionStateMaybeChanged(ConnectionState previous)449 void Peer::BrEdrData::OnConnectionStateMaybeChanged(ConnectionState previous) {
450   if (previous == connection_state()) {
451     return;
452   }
453 
454   bt_log(DEBUG,
455          "gap-bredr",
456          "peer (%s) BR/EDR connection state changed from \"%s\" to \"%s\"",
457          bt_str(peer_->identifier()),
458          ConnectionStateToString(previous).c_str(),
459          ConnectionStateToString(connection_state()).c_str());
460   inspect_properties_.connection_state.Set(
461       ConnectionStateToString(connection_state()));
462 
463   if (connection_state() == ConnectionState::kConnected) {
464     peer_->peer_metrics_->LogBrEdrConnection();
465   } else if (previous == ConnectionState::kConnected) {
466     peer_->peer_metrics_->LogBrEdrDisconnection();
467   }
468 
469   peer_->UpdateExpiry();
470 
471   // Transition to or from kConnected state is a notifyable change.
472   if (previous == ConnectionState::kConnected ||
473       connection_state() == ConnectionState::kConnected) {
474     peer_->UpdatePeerAndNotifyListeners(NotifyListenersChange::kBondNotUpdated);
475   }
476 
477   // Become non-temporary if we successfully connect or are initializing. BR/EDR
478   // device remain non-temporary afterwards if bonded, and temporary again if
479   // disconnect without bonding.
480   if (connection_state() == ConnectionState::kNotConnected) {
481     peer_->TryMakeTemporary();
482   } else {
483     peer_->TryMakeNonTemporary();
484   }
485 }
486 
SetInquiryData(DeviceClass device_class,uint16_t clock_offset,pw::bluetooth::emboss::PageScanRepetitionMode page_scan_rep_mode,int8_t rssi,const BufferView & eir_data)487 void Peer::BrEdrData::SetInquiryData(
488     DeviceClass device_class,
489     uint16_t clock_offset,
490     pw::bluetooth::emboss::PageScanRepetitionMode page_scan_rep_mode,
491     int8_t rssi,
492     const BufferView& eir_data) {
493   peer_->UpdateExpiry();
494 
495   bool notify_listeners = false;
496 
497   // TODO(armansito): Consider sending notifications for RSSI updates perhaps
498   // with throttling to avoid spamming.
499   peer_->SetRssiInternal(rssi);
500 
501   page_scan_rep_mode_ = page_scan_rep_mode;
502   clock_offset_ =
503       pw::bytes::ConvertOrderFrom(cpp20::endian::little, clock_offset) &
504       hci_spec::kClockOffsetMask;
505 
506   if (!device_class_ || *device_class_ != device_class) {
507     device_class_ = device_class;
508     notify_listeners = true;
509   }
510 
511   if (eir_data.size() && SetEirData(eir_data)) {
512     notify_listeners = true;
513   }
514 
515   peer_->OnPeerUpdate();
516 
517   if (notify_listeners) {
518     peer_->NotifyListeners(NotifyListenersChange::kBondNotUpdated);
519   }
520 }
521 
SetEirData(const ByteBuffer & eir)522 bool Peer::BrEdrData::SetEirData(const ByteBuffer& eir) {
523   PW_DCHECK(eir.size());
524 
525   // TODO(armansito): Validate that the EIR data is not malformed?
526   SupplementDataReader reader(eir);
527   DataType type;
528   BufferView data;
529   bool changed = false;
530   while (reader.GetNextField(&type, &data)) {
531     if (type == DataType::kCompleteLocalName) {
532       // TODO(armansito): Parse more fields.
533       // Do not update the name of bonded peers because inquiry results are
534       // unauthenticated.
535       if (!peer_->bonded()) {
536         changed = peer_->RegisterNameInternal(
537             data.ToString(), Peer::NameSource::kInquiryResultComplete);
538       }
539     } else if (type == DataType::kIncomplete16BitServiceUuids ||
540                type == DataType::kComplete16BitServiceUuids) {
541       // TODO(fxbug.dev/42082102): Consider adding 32-bit and 128-bit UUIDs to
542       // the list
543       ParseUuids(
544           data, UUIDElemSize::k16Bit, [this, &changed](const UUID& uuid) {
545             auto [_, inserted] = services_.Mutable()->insert(uuid);
546             if (inserted) {
547               changed = true;
548             }
549             return true;
550           });
551     }
552   }
553   return changed;
554 }
555 
SetBondData(const sm::LTK & link_key)556 bool Peer::BrEdrData::SetBondData(const sm::LTK& link_key) {
557   PW_DCHECK(peer_->connectable());
558 
559   // Do not overwrite an existing key that has greater strength or
560   // authentication.
561   if (link_key_.has_value() &&
562       !link_key.security().IsAsSecureAs(link_key_->security())) {
563     return false;
564   }
565 
566   // Make sure the peer is non-temporary.
567   peer_->TryMakeNonTemporary();
568 
569   // Storing the key establishes the bond.
570   link_key_ = link_key;
571   link_key_.value().AttachInspect(node_, BrEdrData::kInspectLinkKeyName);
572 
573   // PeerCache notifies listeners of new bonds, so no need to request that here.
574   peer_->UpdatePeerAndNotifyListeners(NotifyListenersChange::kBondNotUpdated);
575   return true;
576 }
577 
ClearBondData()578 void Peer::BrEdrData::ClearBondData() {
579   PW_CHECK(link_key_.has_value());
580   link_key_ = std::nullopt;
581 }
582 
AddService(UUID uuid)583 void Peer::BrEdrData::AddService(UUID uuid) {
584   auto [_, inserted] = services_.Mutable()->insert(uuid);
585   if (inserted) {
586     auto update_bond = bonded() ? NotifyListenersChange::kBondUpdated
587                                 : NotifyListenersChange::kBondNotUpdated;
588     peer_->UpdatePeerAndNotifyListeners(update_bond);
589   }
590 }
591 
Peer(NotifyListenersCallback notify_listeners_callback,PeerCallback update_expiry_callback,PeerCallback dual_mode_callback,StoreLowEnergyBondCallback store_le_bond_callback,PeerId identifier,const DeviceAddress & address,bool connectable,PeerMetrics * peer_metrics,pw::async::Dispatcher & dispatcher)592 Peer::Peer(NotifyListenersCallback notify_listeners_callback,
593            PeerCallback update_expiry_callback,
594            PeerCallback dual_mode_callback,
595            StoreLowEnergyBondCallback store_le_bond_callback,
596            PeerId identifier,
597            const DeviceAddress& address,
598            bool connectable,
599            PeerMetrics* peer_metrics,
600            pw::async::Dispatcher& dispatcher)
601     : notify_listeners_callback_(std::move(notify_listeners_callback)),
602       update_expiry_callback_(std::move(update_expiry_callback)),
603       dual_mode_callback_(std::move(dual_mode_callback)),
604       store_le_bond_callback_(std::move(store_le_bond_callback)),
605       identifier_(identifier, MakeToStringInspectConvertFunction()),
606       technology_((address.type() == DeviceAddress::Type::kBREDR)
607                       ? TechnologyType::kClassic
608                       : TechnologyType::kLowEnergy,
609                   [](TechnologyType t) { return TechnologyTypeToString(t); }),
610       address_(address, MakeToStringInspectConvertFunction()),
611       identity_known_(false),
612       name_(std::nullopt,
__anonab3fe1f50c02(const std::optional<PeerName>& v) 613             [](const std::optional<PeerName>& v) {
614               return v ? v->name +
615                              " [source: " + NameSourceToString(v->source) + "]"
616                        : "";
617             }),
618       lmp_version_(std::nullopt,
619                    [](const std::optional<
__anonab3fe1f50d02(const std::optional< pw::bluetooth::emboss::CoreSpecificationVersion>& v) 620                        pw::bluetooth::emboss::CoreSpecificationVersion>& v) {
621                      return v ? hci_spec::HCIVersionToString(*v) : "";
622                    }),
623       lmp_manufacturer_(std::nullopt,
__anonab3fe1f50e02(const std::optional<uint16_t>& m) 624                         [](const std::optional<uint16_t>& m) {
625                           return m ? GetManufacturerName(*m) : "";
626                         }),
627       lmp_features_(hci_spec::LMPFeatureSet(),
628                     MakeToStringInspectConvertFunction()),
629       connectable_(connectable),
630       temporary_(true),
631       rssi_(hci_spec::kRSSIInvalid),
632       peer_metrics_(peer_metrics),
633       last_updated_(dispatcher.now()),
634       dispatcher_(dispatcher),
635       weak_self_(this) {
636   PW_DCHECK(notify_listeners_callback_);
637   PW_DCHECK(update_expiry_callback_);
638   PW_DCHECK(dual_mode_callback_);
639   PW_DCHECK(identifier.IsValid());
640 
641   if (address.type() == DeviceAddress::Type::kBREDR ||
642       address.type() == DeviceAddress::Type::kLEPublic) {
643     identity_known_ = true;
644   }
645 
646   // Initialize transport-specific state.
647   if (*technology_ == TechnologyType::kClassic) {
648     bredr_data_ = BrEdrData(this);
649   } else {
650     le_data_ = LowEnergyData(this);
651   }
652 }
653 
AttachInspect(inspect::Node & parent,std::string node_name)654 void Peer::AttachInspect(inspect::Node& parent, std::string node_name) {
655   node_ = parent.CreateChild(node_name);
656   identifier_.AttachInspect(node_, kInspectPeerIdName);
657   technology_.AttachInspect(node_, kInspectTechnologyName);
658   address_.AttachInspect(node_, kInspectAddressName);
659   name_.AttachInspect(node_, kInspectPeerNameName);
660   lmp_version_.AttachInspect(node_, kInspectVersionName);
661   lmp_manufacturer_.AttachInspect(node_, kInspectManufacturerName);
662   lmp_features_.AttachInspect(node_, kInspectFeaturesName);
663   connectable_.AttachInspect(node_, kInspectConnectableName);
664   temporary_.AttachInspect(node_, kInspectTemporaryName);
665 
666   if (bredr_data_) {
667     bredr_data_->AttachInspect(node_, Peer::BrEdrData::kInspectNodeName);
668   }
669   if (le_data_) {
670     le_data_->AttachInspect(node_, Peer::LowEnergyData::kInspectNodeName);
671   }
672 }
673 
MutLe()674 Peer::LowEnergyData& Peer::MutLe() {
675   if (le_data_) {
676     return *le_data_;
677   }
678 
679   le_data_ = LowEnergyData(this);
680   le_data_->AttachInspect(node_);
681 
682   // Make dual-mode if both transport states have been initialized.
683   if (bredr_data_) {
684     MakeDualMode();
685   }
686   return *le_data_;
687 }
688 
MutBrEdr()689 Peer::BrEdrData& Peer::MutBrEdr() {
690   if (bredr_data_) {
691     return *bredr_data_;
692   }
693 
694   bredr_data_ = BrEdrData(this);
695   bredr_data_->AttachInspect(node_);
696 
697   // Make dual-mode if both transport states have been initialized.
698   if (le_data_) {
699     MakeDualMode();
700   }
701   return *bredr_data_;
702 }
703 
ToString() const704 std::string Peer::ToString() const {
705   return bt_lib_cpp_string::StringPrintf(
706       "{peer id: %s, address: %s}", bt_str(*identifier_), bt_str(*address_));
707 }
708 
RegisterName(const std::string & name,Peer::NameSource source)709 bool Peer::RegisterName(const std::string& name, Peer::NameSource source) {
710   if (RegisterNameInternal(name, source)) {
711     UpdateExpiry();
712     // TODO(fxbug.dev/42140058): Update the bond when this happens
713     UpdatePeerAndNotifyListeners(NotifyListenersChange::kBondNotUpdated);
714     return true;
715   }
716   return false;
717 }
718 
719 // Private methods below:
720 
SetRssiInternal(int8_t rssi)721 bool Peer::SetRssiInternal(int8_t rssi) {
722   if (rssi != hci_spec::kRSSIInvalid && rssi_ != rssi) {
723     rssi_ = rssi;
724     return true;
725   }
726   return false;
727 }
728 
RegisterNameInternal(const std::string & name,Peer::NameSource source)729 bool Peer::RegisterNameInternal(const std::string& name,
730                                 Peer::NameSource source) {
731   if (!pw::utf8::IsStringValid(name)) {
732     bt_log(WARN,
733            "gap",
734            "%s: not setting name to string that is not valid UTF-8",
735            bt_str(*this));
736     return false;
737   }
738   if (!name_->has_value() || source < (*name_)->source ||
739       (source == (*name_)->source && name != (*name_)->name)) {
740     name_.Set(Peer::PeerName{name, source});
741     return true;
742   }
743   return false;
744 }
745 
TryMakeNonTemporary()746 bool Peer::TryMakeNonTemporary() {
747   // TODO(armansito): Since we don't currently support address resolution,
748   // random addresses should never be persisted.
749   if (!connectable()) {
750     bt_log(DEBUG, "gap", "remains temporary: %s", bt_str(*this));
751     return false;
752   }
753 
754   bt_log(DEBUG, "gap", "became non-temporary: %s:", bt_str(*this));
755 
756   if (*temporary_) {
757     temporary_.Set(false);
758     UpdateExpiry();
759     UpdatePeerAndNotifyListeners(NotifyListenersChange::kBondNotUpdated);
760   }
761 
762   return true;
763 }
764 
TryMakeTemporary()765 bool Peer::TryMakeTemporary() {
766   if (le() && le()->connection_state() == ConnectionState::kNotConnected &&
767       !identity_known()) {
768     bt_log(DEBUG, "gap", "LE became temporary: %s:", bt_str(*this));
769     temporary_.Set(true);
770     return true;
771   }
772   if (bredr() && !bredr()->bonded()) {
773     bt_log(DEBUG, "gap", "BR/EDR became temporary: %s:", bt_str(*this));
774     temporary_.Set(true);
775     return true;
776   }
777   return false;
778 }
779 
UpdateExpiry()780 void Peer::UpdateExpiry() {
781   PW_DCHECK(update_expiry_callback_);
782   update_expiry_callback_(*this);
783 }
784 
NotifyListeners(NotifyListenersChange change)785 void Peer::NotifyListeners(NotifyListenersChange change) {
786   PW_DCHECK(notify_listeners_callback_);
787   notify_listeners_callback_(*this, change);
788 }
789 
MakeDualMode()790 void Peer::MakeDualMode() {
791   technology_.Set(TechnologyType::kDualMode);
792   PW_DCHECK(dual_mode_callback_);
793   dual_mode_callback_(*this);
794 }
795 
OnPeerUpdate()796 void Peer::OnPeerUpdate() { last_updated_ = dispatcher_.now(); }
797 
UpdatePeerAndNotifyListeners(NotifyListenersChange change)798 void Peer::UpdatePeerAndNotifyListeners(NotifyListenersChange change) {
799   OnPeerUpdate();
800   NotifyListeners(change);
801 }
802 
803 }  // namespace bt::gap
804