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 #pragma once 16 #include <lib/fit/defer.h> 17 18 #include <string> 19 #include <unordered_set> 20 21 #include "pw_bluetooth_sapphire/internal/host/common/advertising_data.h" 22 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h" 23 #include "pw_bluetooth_sapphire/internal/host/common/device_address.h" 24 #include "pw_bluetooth_sapphire/internal/host/common/inspectable.h" 25 #include "pw_bluetooth_sapphire/internal/host/common/macros.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/gap/peer_metrics.h" 29 #include "pw_bluetooth_sapphire/internal/host/gatt/persisted_data.h" 30 #include "pw_bluetooth_sapphire/internal/host/hci-spec/constants.h" 31 #include "pw_bluetooth_sapphire/internal/host/hci-spec/le_connection_parameters.h" 32 #include "pw_bluetooth_sapphire/internal/host/hci-spec/lmp_feature_set.h" 33 #include "pw_bluetooth_sapphire/internal/host/hci/connection.h" 34 #include "pw_bluetooth_sapphire/internal/host/sm/types.h" 35 36 namespace bt::gap { 37 38 class PeerCache; 39 40 // Represents a remote Bluetooth device that is known to the current system due 41 // to discovery and/or connection and bonding procedures. These devices can be 42 // LE-only, Classic-only, or dual-mode. 43 // 44 // Instances should not be created directly and must be obtained via a 45 // PeerCache. 46 class Peer final { 47 public: 48 using PeerCallback = fit::function<void(const Peer&)>; 49 50 // Describes the change(s) that caused the peer to notify listeners. 51 enum class NotifyListenersChange { 52 kBondNotUpdated, // No persistent data has changed 53 kBondUpdated, // Persistent data has changed 54 }; 55 using NotifyListenersCallback = 56 fit::function<void(const Peer&, NotifyListenersChange)>; 57 58 using StoreLowEnergyBondCallback = 59 fit::function<bool(const sm::PairingData&)>; 60 61 // Caller must ensure that callbacks are non-empty. 62 // Note that the ctor is only intended for use by PeerCache. 63 // Expanding access would a) violate the constraint that all Peers 64 // are created through a PeerCache, and b) introduce lifetime issues 65 // (do the callbacks outlive |this|?). 66 Peer(NotifyListenersCallback notify_listeners_callback, 67 PeerCallback update_expiry_callback, 68 PeerCallback dual_mode_callback, 69 StoreLowEnergyBondCallback store_le_bond_callback, 70 PeerId identifier, 71 const DeviceAddress& address, 72 bool connectable, 73 PeerMetrics* peer_metrics, 74 pw::async::Dispatcher& dispatcher); 75 IsSecureSimplePairingSupported()76 bool IsSecureSimplePairingSupported() { 77 return lmp_features_->HasBit( 78 /*page=*/0, 79 hci_spec::LMPFeature::kSecureSimplePairingControllerSupport) && 80 lmp_features_->HasBit( 81 /*page=*/1, 82 hci_spec::LMPFeature::kSecureSimplePairingHostSupport); 83 } 84 85 // Connection state as considered by the GAP layer. This may not correspond 86 // exactly with the presence or absence of a link at the link layer. For 87 // example, GAP may consider a peer disconnected whilst the link disconnection 88 // procedure is still continuing. 89 enum class ConnectionState { 90 // No link exists between the local adapter and peer or link is being torn 91 // down (disconnection command has been sent). 92 kNotConnected, 93 94 // Currently establishing a link, performing service discovery, or 95 // setting up encryption. In this state, a link may have been 96 // established but it is not ready to use yet. 97 kInitializing, 98 99 // Link setup, service discovery, and any encryption setup has completed 100 kConnected 101 }; 102 static std::string ConnectionStateToString(Peer::ConnectionState); 103 104 // Description of auto-connect behaviors. 105 // 106 // By default, the stack will auto-connect to any bonded devices as soon as 107 // soon as they become available. 108 enum class AutoConnectBehavior { 109 // Always auto-connect device when possible. 110 kAlways, 111 112 // Ignore auto-connection possibilities, but reset to kAlways after the next 113 // manual connection. 114 kSkipUntilNextConnection, 115 }; 116 117 // This device's name can be read from various sources: LE advertisements, 118 // Inquiry results, Name Discovery Procedure, the GAP service, or from a 119 // restored bond. When a name is read, it should be registered along with its 120 // source location. `RegisterName()` will update the device name attribute if 121 // the newly encountered name's source is of higher priority (lower enum 122 // value) than that of the existing name. 123 enum class NameSource { 124 kGenericAccessService = /*highest priority*/ 0, 125 kNameDiscoveryProcedure = 1, 126 kInquiryResultComplete = 2, 127 kAdvertisingDataComplete = 3, 128 kInquiryResultShortened = 4, 129 kAdvertisingDataShortened = 5, 130 kUnknown = /*lowest priority*/ 6, 131 }; 132 static std::string NameSourceToString(Peer::NameSource); 133 134 static constexpr const char* kInspectPeerIdName = "peer_id"; 135 static constexpr const char* kInspectPeerNameName = "name"; 136 static constexpr const char* kInspectTechnologyName = "technology"; 137 static constexpr const char* kInspectAddressName = "address"; 138 static constexpr const char* kInspectConnectableName = "connectable"; 139 static constexpr const char* kInspectTemporaryName = "temporary"; 140 static constexpr const char* kInspectFeaturesName = "features"; 141 static constexpr const char* kInspectVersionName = "hci_version"; 142 static constexpr const char* kInspectManufacturerName = "manufacturer"; 143 144 // Attach peer as child node of |parent| with specified |name|. 145 void AttachInspect(inspect::Node& parent, std::string name = "peer"); 146 147 enum class TokenType { kInitializing, kConnection }; 148 template <TokenType T> 149 class TokenWithCallback { 150 public: TokenWithCallback(fit::callback<void ()> on_destruction)151 explicit TokenWithCallback(fit::callback<void()> on_destruction) 152 : on_destruction_(std::move(on_destruction)) {} 153 ~TokenWithCallback() = default; 154 TokenWithCallback(TokenWithCallback&&) noexcept = default; 155 TokenWithCallback& operator=(TokenWithCallback&&) noexcept = default; 156 157 private: 158 fit::deferred_callback on_destruction_; 159 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(TokenWithCallback); 160 }; 161 162 // InitializingConnectionToken is meant to be held by a connection request 163 // object. When the request object is destroyed, the specified callback will 164 // be called to update the connection state. 165 using InitializingConnectionToken = 166 TokenWithCallback<TokenType::kInitializing>; 167 168 // ConnectionToken is meant to be held by a connection object. When the 169 // connection object is destroyed, the specified callback will be called to 170 // update the connection state. 171 using ConnectionToken = TokenWithCallback<TokenType::kConnection>; 172 173 // Contains Peer data that apply only to the LE transport. 174 class LowEnergyData final { 175 public: 176 static constexpr const char* kInspectNodeName = "le_data"; 177 static constexpr const char* kInspectConnectionStateName = 178 "connection_state"; 179 static constexpr const char* kInspectAdvertisingDataParseFailureCountName = 180 "adv_data_parse_failure_count"; 181 static constexpr const char* kInspectLastAdvertisingDataParseFailureName = 182 "last_adv_data_parse_failure"; 183 static constexpr const char* kInspectBondDataName = "bonded"; 184 static constexpr const char* kInspectFeaturesName = "features"; 185 186 explicit LowEnergyData(Peer* owner); 187 188 void AttachInspect(inspect::Node& parent, 189 std::string name = kInspectNodeName); 190 191 // Current connection state. connection_state()192 ConnectionState connection_state() const { 193 return connected() ? ConnectionState::kConnected 194 : initializing() ? ConnectionState::kInitializing 195 : ConnectionState::kNotConnected; 196 } connected()197 bool connected() const { return connection_tokens_count_ > 0; } initializing()198 bool initializing() const { 199 return !connected() && initializing_tokens_count_ > 0; 200 } 201 bonded()202 bool bonded() const { return bond_data_->has_value(); } should_auto_connect()203 bool should_auto_connect() const { 204 return bonded() && auto_conn_behavior_ == AutoConnectBehavior::kAlways; 205 } 206 207 // Note that it is possible for `advertising_data()` to return a non-empty 208 // buffer while this method returns std::nullopt, as AdvertisingData is only 209 // stored if it is parsed correctly. 210 // TODO(fxbug.dev/42166259): Migrate clients off of advertising_data, so 211 // that we do not need to store the raw buffer after parsing it. 212 const std::optional<std::reference_wrapper<const AdvertisingData>> parsed_advertising_data()213 parsed_advertising_data() const { 214 if (parsed_adv_data_.is_error()) { 215 return std::nullopt; 216 } 217 return std::cref(parsed_adv_data_.value()); 218 } 219 // Returns the timestamp associated with the most recently successfully 220 // parsed AdvertisingData. 221 std::optional<pw::chrono::SystemClock::time_point> parsed_advertising_data_timestamp()222 parsed_advertising_data_timestamp() const { 223 return adv_timestamp_; 224 } 225 226 // Returns the error, if any, encountered when parsing the advertising data 227 // from the peer. advertising_data_error()228 std::optional<AdvertisingData::ParseError> advertising_data_error() const { 229 if (!parsed_adv_data_.is_error()) { 230 return std::nullopt; 231 } 232 return parsed_adv_data_.error_value(); 233 } 234 235 // Most recently used LE connection parameters. Has no value if the peer 236 // has never been connected. 237 const std::optional<hci_spec::LEConnectionParameters>& connection_parameters()238 connection_parameters() const { 239 return conn_params_; 240 } 241 242 // Preferred LE connection parameters as reported by the peer. 243 const std::optional<hci_spec::LEPreferredConnectionParameters>& preferred_connection_parameters()244 preferred_connection_parameters() const { 245 return preferred_conn_params_; 246 } 247 248 // This peer's LE bond data, if bonded. bond_data()249 const std::optional<sm::PairingData>& bond_data() const { 250 return *bond_data_; 251 } 252 feature_interrogation_complete()253 bool feature_interrogation_complete() const { 254 return feature_interrogation_complete_; 255 } 256 257 // Bit mask of LE features (Core Spec v5.2, Vol 6, Part B, Section 4.6). features()258 std::optional<hci_spec::LESupportedFeatures> features() const { 259 return *features_; 260 } 261 262 // Setters: 263 264 // Overwrites the stored advertising and scan response data with the 265 // contents of |data| and updates the known RSSI and timestamp with the 266 // given values. 267 void SetAdvertisingData(int8_t rssi, 268 const ByteBuffer& data, 269 pw::chrono::SystemClock::time_point timestamp); 270 271 // Register a connection that is in the request/initializing state. A token 272 // is returned that should be owned until the initialization is complete or 273 // canceled. The connection state may be updated and listeners may be 274 // notified. Multiple initializating connections may be registered. 275 [[nodiscard]] InitializingConnectionToken RegisterInitializingConnection(); 276 277 // Register a connection that is in the connected state. A token is returned 278 // that should be owned until the connection is disconnected. The connection 279 // state may be updated and listeners may be notified. Multiple connections 280 // may be registered. 281 [[nodiscard]] ConnectionToken RegisterConnection(); 282 283 // Modify the current or preferred connection parameters. 284 // The device must be connectable. 285 void SetConnectionParameters(const hci_spec::LEConnectionParameters& value); 286 void SetPreferredConnectionParameters( 287 const hci_spec::LEPreferredConnectionParameters& value); 288 289 // Stores the bond in PeerCache, which updates the address map and calls 290 // SetBondData. 291 bool StoreBond(const sm::PairingData& bond_data); 292 293 // Stores LE bonding data and makes this "bonded." 294 // Marks as non-temporary if necessary. 295 // This should only be called by PeerCache. 296 void SetBondData(const sm::PairingData& bond_data); 297 298 // Removes any stored keys. Does not make the peer temporary, even if it 299 // is disconnected. Does not notify listeners. 300 void ClearBondData(); 301 SetFeatureInterrogationComplete()302 void SetFeatureInterrogationComplete() { 303 feature_interrogation_complete_ = true; 304 } 305 SetFeatures(hci_spec::LESupportedFeatures features)306 void SetFeatures(hci_spec::LESupportedFeatures features) { 307 features_.Set(features); 308 } 309 310 // Get pieces of the GATT database that must be persisted for bonded peers. get_service_changed_gatt_data()311 const gatt::ServiceChangedCCCPersistedData& get_service_changed_gatt_data() 312 const { 313 return service_changed_gatt_data_; 314 } 315 316 // Set pieces of the GATT database that must be persisted for bonded peers. set_service_changed_gatt_data(const gatt::ServiceChangedCCCPersistedData & gatt_data)317 void set_service_changed_gatt_data( 318 const gatt::ServiceChangedCCCPersistedData& gatt_data) { 319 service_changed_gatt_data_ = gatt_data; 320 } 321 set_auto_connect_behavior(AutoConnectBehavior behavior)322 void set_auto_connect_behavior(AutoConnectBehavior behavior) { 323 auto_conn_behavior_ = behavior; 324 } 325 set_sleep_clock_accuracy(pw::bluetooth::emboss::LESleepClockAccuracyRange sca)326 void set_sleep_clock_accuracy( 327 pw::bluetooth::emboss::LESleepClockAccuracyRange sca) { 328 sleep_clock_accuracy_ = sca; 329 } 330 331 std::optional<pw::bluetooth::emboss::LESleepClockAccuracyRange> sleep_clock_accuracy()332 sleep_clock_accuracy() const { 333 return sleep_clock_accuracy_; 334 } 335 336 // TODO(armansito): Store most recently seen random address and identity 337 // address separately, once PeerCache can index peers by multiple 338 // addresses. 339 340 private: 341 struct InspectProperties { 342 inspect::StringProperty connection_state; 343 inspect::StringProperty last_adv_data_parse_failure; 344 }; 345 346 // Called when the connection state changes. 347 void OnConnectionStateMaybeChanged(ConnectionState previous); 348 349 Peer* peer_; // weak 350 351 inspect::Node node_; 352 InspectProperties inspect_properties_; 353 354 uint16_t initializing_tokens_count_ = 0; 355 uint16_t connection_tokens_count_ = 0; 356 std::optional<hci_spec::LEConnectionParameters> conn_params_; 357 std::optional<hci_spec::LEPreferredConnectionParameters> 358 preferred_conn_params_; 359 360 // Buffer containing advertising and scan response data appended to each 361 // other. NOTE: Repeated fields in advertising and scan response data are 362 // not deduplicated, so duplicate entries are possible. It is OK to assume 363 // that fields repeated in scan response data supersede those in the 364 // original advertising data when processing fields in order. 365 DynamicByteBuffer adv_data_buffer_; 366 // Time when advertising data was last updated and successfully parsed. 367 std::optional<pw::chrono::SystemClock::time_point> adv_timestamp_; 368 // AdvertisingData parsed from the peer's advertising data, if parsed 369 // correctly. 370 AdvertisingData::ParseResult parsed_adv_data_ = 371 fit::error(AdvertisingData::ParseError::kMissing); 372 373 BoolInspectable<std::optional<sm::PairingData>> bond_data_; 374 375 IntInspectable<int64_t> adv_data_parse_failure_count_; 376 377 AutoConnectBehavior auto_conn_behavior_ = AutoConnectBehavior::kAlways; 378 379 bool feature_interrogation_complete_ = false; 380 381 // features_ will be unset if feature interrogation has not been attempted 382 // (in which case feature_interrogation_complete_ will be false) or if 383 // feature interrogation has failed (in which case 384 // feature_interrogation_complete_ will be true). 385 StringInspectable<std::optional<hci_spec::LESupportedFeatures>> features_; 386 387 // TODO(armansito): Store GATT service UUIDs. 388 389 // Data persisted from GATT database for bonded peers. 390 gatt::ServiceChangedCCCPersistedData service_changed_gatt_data_; 391 392 std::optional<pw::bluetooth::emboss::LESleepClockAccuracyRange> 393 sleep_clock_accuracy_; 394 }; 395 396 // Contains Peer data that apply only to the BR/EDR transport. 397 class BrEdrData final { 398 public: 399 static constexpr const char* kInspectNodeName = "bredr_data"; 400 static constexpr const char* kInspectConnectionStateName = 401 "connection_state"; 402 static constexpr const char* kInspectLinkKeyName = "link_key"; 403 static constexpr const char* kInspectServicesName = "services"; 404 405 explicit BrEdrData(Peer* owner); 406 407 // Attach peer inspect node as a child node of |parent|. 408 void AttachInspect(inspect::Node& parent, 409 std::string name = kInspectNodeName); 410 411 // Current connection state. connection_state()412 ConnectionState connection_state() const { 413 if (connected()) { 414 return ConnectionState::kConnected; 415 } 416 if (initializing()) { 417 return ConnectionState::kInitializing; 418 } 419 return ConnectionState::kNotConnected; 420 } connected()421 bool connected() const { 422 return !initializing() && connection_tokens_count_ > 0; 423 } initializing()424 bool initializing() const { return initializing_tokens_count_ > 0; } 425 bonded()426 bool bonded() const { return link_key_.has_value(); } 427 428 // Returns the peer's BD_ADDR. address()429 const DeviceAddress& address() const { return address_; } 430 431 // Returns the device class reported by the peer, if it is known. device_class()432 const std::optional<DeviceClass>& device_class() const { 433 return device_class_; 434 } 435 436 // Returns the page scan repetition mode of the peer, if known. 437 const std::optional<pw::bluetooth::emboss::PageScanRepetitionMode>& page_scan_repetition_mode()438 page_scan_repetition_mode() const { 439 return page_scan_rep_mode_; 440 } 441 442 // Returns the clock offset reported by the peer, if known and valid. The 443 // clock offset will NOT have the highest-order bit set and the rest 444 // represents bits 16-2 of CLKNPeripheral-CLK (see 445 // hci_spec::kClockOffsetFlagBit in hci/hci_constants.h). clock_offset()446 const std::optional<uint16_t>& clock_offset() const { 447 return clock_offset_; 448 } 449 link_key()450 const std::optional<sm::LTK>& link_key() const { return link_key_; } 451 services()452 const std::unordered_set<UUID>& services() const { return *services_; } 453 454 // Setters: 455 456 // Updates the inquiry data and notifies listeners. These 457 // methods expect HCI inquiry result structures as they are obtained from 458 // the Bluetooth controller. Each field should be encoded in little-endian 459 // byte order. 460 void SetInquiryData(const pw::bluetooth::emboss::InquiryResultView& view); 461 void SetInquiryData( 462 const pw::bluetooth::emboss::InquiryResultWithRssiView& view); 463 void SetInquiryData( 464 const pw::bluetooth::emboss::ExtendedInquiryResultEventView& view); 465 466 // Sets the data from an incoming connection from this peer. 467 void SetIncomingRequest( 468 const pw::bluetooth::emboss::ConnectionRequestEventView& view); 469 470 // Register a connection that is in the request/initializing state. A token 471 // is returned that should be owned until the initialization is complete or 472 // canceled. The connection state may be updated and listeners may be 473 // notified. Multiple initializating connections may be registered. 474 [[nodiscard]] InitializingConnectionToken RegisterInitializingConnection(); 475 476 // Register a connection that is in the connected state. A token is returned 477 // that should be owned until the connection is disconnected. The connection 478 // state may be updated and listeners may be notified. Only one connection 479 // may be registered at a time (enforced by assertion). 480 [[nodiscard]] ConnectionToken RegisterConnection(); 481 482 // Stores a link key resulting from Secure Simple Pairing and makes this 483 // peer "bonded." Marks the peer as non-temporary if necessary. All 484 // BR/EDR link keys are "long term" (reusable across sessions). 485 void SetBondData(const sm::LTK& link_key); 486 487 // Removes any stored link key. Does not make the device temporary, even if 488 // it is disconnected. Does not notify listeners. 489 void ClearBondData(); 490 491 // Adds a service discovered on the peer, identified by |uuid|, then 492 // notifies listeners. No-op if already present. 493 void AddService(UUID uuid); 494 495 // TODO(armansito): Store BD_ADDR here, once PeerCache can index 496 // devices by multiple addresses. 497 498 private: 499 struct InspectProperties { 500 inspect::StringProperty connection_state; 501 }; 502 503 // Called when the connection state changes. 504 void OnConnectionStateMaybeChanged(ConnectionState previous); 505 506 // All multi-byte fields must be in little-endian byte order as they were 507 // received from the controller. 508 void SetInquiryData( 509 DeviceClass device_class, 510 uint16_t clock_offset, 511 pw::bluetooth::emboss::PageScanRepetitionMode page_scan_rep_mode, 512 int8_t rssi = hci_spec::kRSSIInvalid, 513 const BufferView& eir_data = BufferView()); 514 515 // Updates the EIR data field and returns true if any properties changed. 516 bool SetEirData(const ByteBuffer& data); 517 518 Peer* peer_; // weak 519 inspect::Node node_; 520 InspectProperties inspect_properties_; 521 522 uint16_t initializing_tokens_count_ = 0; 523 uint16_t connection_tokens_count_ = 0; 524 525 DeviceAddress address_; 526 std::optional<DeviceClass> device_class_; 527 std::optional<pw::bluetooth::emboss::PageScanRepetitionMode> 528 page_scan_rep_mode_; 529 std::optional<uint16_t> clock_offset_; 530 531 std::optional<sm::LTK> link_key_; 532 533 StringInspectable<std::unordered_set<UUID>> services_; 534 }; 535 536 // Number that uniquely identifies this device with respect to the bt-host 537 // that generated it. 538 // TODO(armansito): Come up with a scheme that guarnatees the uniqueness of 539 // this ID across all bt-hosts. Today this is guaranteed since we don't allow 540 // clients to interact with multiple controllers simultaneously though this 541 // could possibly lead to collisions if the active adapter gets changed 542 // without clearing the previous adapter's cache. identifier()543 PeerId identifier() const { return *identifier_; } 544 545 // The Bluetooth technologies that are supported by this device. technology()546 TechnologyType technology() const { return *technology_; } 547 548 // The known device address of this device. Depending on the technologies 549 // supported by this device this has the following meaning: 550 // 551 // * For BR/EDR devices this is the BD_ADDR. 552 // 553 // * For LE devices this is identity address IF identity_known() returns 554 // true. This is always the case if the address type is LE Public. 555 // 556 // For LE devices that use privacy, identity_known() will be set to false 557 // upon discovery. The address will be updated only once the identity 558 // address has been obtained during the pairing procedure. 559 // 560 // * For BR/EDR/LE devices this is the BD_ADDR and the LE identity address. 561 // If a BR/EDR/LE device uses an identity address that is different from 562 // its BD_ADDR, then there will be two separate Peer entries for 563 // it. address()564 const DeviceAddress& address() const { return *address_; } identity_known()565 bool identity_known() const { return identity_known_; } 566 567 // The LMP version of this device obtained doing discovery. 568 const std::optional<pw::bluetooth::emboss::CoreSpecificationVersion>& version()569 version() const { 570 return *lmp_version_; 571 } 572 573 // Returns true if this is a connectable device. connectable()574 bool connectable() const { return *connectable_; } 575 576 // Returns true if this device is connected over BR/EDR or LE transports. connected()577 bool connected() const { 578 return (le() && le()->connected()) || (bredr() && bredr()->connected()); 579 } 580 581 // Returns true if this device has been bonded over BR/EDR or LE transports. bonded()582 bool bonded() const { 583 return (le() && le()->bonded()) || (bredr() && bredr()->bonded()); 584 } 585 586 // Returns the most recently observed RSSI for this peer. Returns 587 // hci_spec::kRSSIInvalid if the value is unknown. rssi()588 int8_t rssi() const { return rssi_; } 589 590 // Gets the user-friendly name of the device, if it's known. name()591 std::optional<std::string> name() const { 592 return name_->has_value() ? std::optional<std::string>{(*name_)->name} 593 : std::nullopt; 594 } 595 596 // Gets the source from which this peer's name was read, if it's known. name_source()597 std::optional<NameSource> name_source() const { 598 return name_->has_value() ? std::optional<NameSource>{(*name_)->source} 599 : std::nullopt; 600 } 601 602 // Gets the appearance of the device, if it's known. appearance()603 const std::optional<uint16_t>& appearance() const { return appearance_; } 604 605 // Returns the set of features of this device. features()606 const hci_spec::LMPFeatureSet& features() const { return *lmp_features_; } 607 608 // A temporary device gets removed from the PeerCache after a period 609 // of inactivity (see the |update_expiry_callback| argument to the 610 // constructor). The following rules determine the temporary state of a 611 // device: 612 // a. A device is temporary by default. 613 // b. A device becomes non-temporary when it gets connected. 614 // c. A device becomes temporary again when disconnected only if its 615 // identity is not known (i.e. identity_known() returns false). This only 616 // applies to LE devices that use the privacy feature. 617 // 618 // Temporary devices are never bonded. temporary()619 bool temporary() const { return *temporary_; } 620 621 // Returns the LE transport specific data of this device, if any. This will be 622 // present if information about this device is obtained using the LE discovery 623 // and connection procedures. le()624 const std::optional<LowEnergyData>& le() const { return le_data_; } 625 626 // Returns the BR/EDR transport specific data of this device, if any. This 627 // will be present if information about this device is obtained using the 628 // BR/EDR discovery and connection procedures. bredr()629 const std::optional<BrEdrData>& bredr() const { return bredr_data_; } 630 631 // Returns a mutable reference to each transport-specific data structure, 632 // initializing the structure if it is unitialized. Use these to mutate 633 // members of the transport-specific structs. The caller must make sure to 634 // invoke these only if the device is known to support said technology. 635 LowEnergyData& MutLe(); 636 BrEdrData& MutBrEdr(); 637 638 // Returns a string representation of this device. 639 std::string ToString() const; 640 641 // The following methods mutate Peer properties: 642 643 // Updates the name of this device if no name is currently set or if the 644 // source of `name` has higher priority than that of the existing name. 645 // Returns true if a name change occurs. If the name is updated and 646 // `notify_listeners` is false, then listeners will not be notified of an 647 // update to this peer. 648 bool RegisterName(const std::string& name, 649 NameSource source = NameSource::kUnknown); 650 651 // Updates the appearance of this device. SetAppearance(uint16_t appearance)652 void SetAppearance(uint16_t appearance) { appearance_ = appearance; } 653 654 // Sets the value of the LMP |features| for the given |page| number. SetFeaturePage(size_t page,uint64_t features)655 void SetFeaturePage(size_t page, uint64_t features) { 656 lmp_features_.Mutable()->SetPage(page, features); 657 } 658 659 // Sets the last available LMP feature |page| number for this device. set_last_page_number(uint8_t page)660 void set_last_page_number(uint8_t page) { 661 lmp_features_.Mutable()->set_last_page_number(page); 662 } 663 set_version(pw::bluetooth::emboss::CoreSpecificationVersion version,uint16_t manufacturer,uint16_t subversion)664 void set_version(pw::bluetooth::emboss::CoreSpecificationVersion version, 665 uint16_t manufacturer, 666 uint16_t subversion) { 667 lmp_version_.Set(version); 668 lmp_manufacturer_.Set(manufacturer); 669 lmp_subversion_ = subversion; 670 } 671 672 // Marks this device's identity as known. Called by PeerCache when 673 // initializing a bonded device and by LowEnergyData when setting bond data 674 // with an identity address. set_identity_known(bool value)675 void set_identity_known(bool value) { identity_known_ = value; } 676 677 // Stores the BR/EDR cross-transport key generated through LE pairing. This 678 // method will not mark the peer as dual-mode if it is not already dual-mode. 679 // It will also not overwrite existing BR/EDR link keys of stronger security 680 // than `ct_key`. 681 void StoreBrEdrCrossTransportKey(sm::LTK ct_key); 682 683 // Update the connectable status of this peer. This is useful if the peer 684 // sends both non-connectable and connectable advertisements (e.g. when it is 685 // a beacon). set_connectable(bool connectable)686 void set_connectable(bool connectable) { connectable_.Set(connectable); } 687 688 // The time when the most recent update occurred. Updates include: 689 // * LE advertising data updated 690 // * LE connection state updated 691 // * LE bond state updated 692 // * BR/EDR connection state updated 693 // * BR/EDR inquiry data updated 694 // * BR/EDR bond data updated 695 // * BR/EDR services updated 696 // * name is updated last_updated()697 pw::chrono::SystemClock::time_point last_updated() const { 698 return last_updated_; 699 } 700 701 using WeakPtr = WeakSelf<Peer>::WeakPtr; GetWeakPtr()702 Peer::WeakPtr GetWeakPtr() { return weak_self_.GetWeakPtr(); } 703 704 private: 705 struct PeerName { 706 std::string name; 707 NameSource source; 708 }; 709 710 // Assigns a new value for the address of this device. Called by LowEnergyData 711 // when a new identity address is assigned. set_address(const DeviceAddress & address)712 void set_address(const DeviceAddress& address) { address_.Set(address); } 713 714 // Updates the RSSI and returns true if it changed. 715 bool SetRssiInternal(int8_t rssi); 716 717 // Updates the name and returns true if there was a change without notifying 718 // listeners. 719 // TODO(armansito): Add similarly styled internal setters so that we can batch 720 // more updates. 721 bool RegisterNameInternal(const std::string& name, NameSource source); 722 723 // Marks this device as non-temporary. This operation may fail due to one of 724 // the conditions described above the |temporary()| method. 725 // 726 // TODO(armansito): Replace this with something more sophisticated when we 727 // implement bonding procedures. This method is here to remind us that these 728 // conditions are subtle and not fully supported yet. 729 bool TryMakeNonTemporary(); 730 731 // Marks this device as temporary. This operation may fail due to one of 732 // the conditions described above the |temporary()| method. 733 bool TryMakeTemporary(); 734 735 // Tells the owning PeerCache to update the expiry state of this 736 // device. 737 void UpdateExpiry(); 738 739 // Signal to the cache to notify listeners. 740 void NotifyListeners(NotifyListenersChange change); 741 742 // Mark this device as dual mode and signal the cache. 743 void MakeDualMode(); 744 745 // Updates the peer last updated timestamp. 746 void OnPeerUpdate(); 747 748 // Updates the peer last updated timestamp an notifies listeners. 749 void UpdatePeerAndNotifyListeners(NotifyListenersChange change); 750 751 inspect::Node node_; 752 753 // Callbacks used to notify state changes. 754 NotifyListenersCallback notify_listeners_callback_; 755 PeerCallback update_expiry_callback_; 756 PeerCallback dual_mode_callback_; 757 StoreLowEnergyBondCallback store_le_bond_callback_; 758 759 StringInspectable<PeerId> identifier_; 760 StringInspectable<TechnologyType> technology_; 761 762 StringInspectable<DeviceAddress> address_; 763 bool identity_known_; 764 765 StringInspectable<std::optional<PeerName>> name_; 766 // TODO(fxbug.dev/42177971): Coordinate this field with the appearance read 767 // from advertising data. 768 std::optional<uint16_t> appearance_; 769 StringInspectable< 770 std::optional<pw::bluetooth::emboss::CoreSpecificationVersion>> 771 lmp_version_; 772 StringInspectable<std::optional<uint16_t>> lmp_manufacturer_; 773 std::optional<uint16_t> lmp_subversion_; 774 StringInspectable<hci_spec::LMPFeatureSet> lmp_features_; 775 BoolInspectable<bool> connectable_; 776 BoolInspectable<bool> temporary_; 777 int8_t rssi_; 778 779 // The spec does not explicitly prohibit LE->BREDR cross-transport key 780 // generation for LE-only peers. This is used to store a CT-generated BR/EDR 781 // key for LE-only peers to avoid incorrectly marking a peer as dual-mode. 782 std::optional<sm::LTK> bredr_cross_transport_key_; 783 784 // Data that only applies to the LE transport. This is present if this device 785 // is known to support LE. 786 std::optional<LowEnergyData> le_data_; 787 788 // Data that only applies to the BR/EDR transport. This is present if this 789 // device is known to support BR/EDR. 790 std::optional<BrEdrData> bredr_data_; 791 792 // Metrics counters used across all peer objects. Weak reference. 793 PeerMetrics* peer_metrics_; 794 795 // The time when the most recent update occurred. 796 pw::chrono::SystemClock::time_point last_updated_; 797 798 pw::async::Dispatcher& dispatcher_; 799 800 WeakSelf<Peer> weak_self_; 801 802 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Peer); 803 }; 804 805 } // namespace bt::gap 806