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