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