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