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