Skip to content

Commit

Permalink
pw_bluetooth_sapphire: Add PairingTokens to Peer
Browse files Browse the repository at this point in the history
Add PairingTokens to Peer to facilitate cross-transport pairing
synchronization for CTKD.

Bug: 388607971
Change-Id: I917b3ab35b86d78649342f4f0745865671bda3a8
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/259112
Lint: Lint 🤖 <[email protected]>
Reviewed-by: Jason Graffius <[email protected]>
Docs-Not-Needed: Ben Lawson <[email protected]>
Commit-Queue: Auto-Submit <[email protected]>
Pigweed-Auto-Submit: Ben Lawson <[email protected]>
  • Loading branch information
BenjaminLawson authored and CQ Bot Account committed Jan 10, 2025
1 parent 3cb02e2 commit 6148e5f
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 6 deletions.
65 changes: 65 additions & 0 deletions pw_bluetooth_sapphire/host/gap/peer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,39 @@ Peer::ConnectionToken Peer::LowEnergyData::RegisterConnection() {
return ConnectionToken(std::move(unregister_cb));
}

Peer::PairingToken Peer::LowEnergyData::RegisterPairing() {
pairing_tokens_count_++;
auto unregister_cb = [self = peer_->GetWeakPtr(), this] {
if (!self.is_alive()) {
return;
}
pairing_tokens_count_--;
OnPairingMaybeComplete();
};
return PairingToken(std::move(unregister_cb));
}

bool Peer::LowEnergyData::is_pairing() const {
return pairing_tokens_count_ > 0;
}

void Peer::LowEnergyData::add_pairing_completion_callback(
fit::callback<void()>&& callback) {
pairing_complete_callbacks_.emplace_back(std::move(callback));
OnPairingMaybeComplete();
}

void Peer::LowEnergyData::OnPairingMaybeComplete() {
if (pairing_tokens_count_ > 0 || pairing_complete_callbacks_.empty()) {
return;
}
std::vector<fit::callback<void()>> callbacks;
std::swap(callbacks, pairing_complete_callbacks_);
for (auto& cb : callbacks) {
cb();
}
}

void Peer::LowEnergyData::SetConnectionParameters(
const hci_spec::LEConnectionParameters& params) {
PW_DCHECK(peer_->connectable());
Expand Down Expand Up @@ -380,6 +413,38 @@ Peer::ConnectionToken Peer::BrEdrData::RegisterConnection() {
});
}

Peer::PairingToken Peer::BrEdrData::RegisterPairing() {
PW_CHECK(!is_pairing());
pairing_tokens_count_++;
auto unregister_cb = [self = peer_->GetWeakPtr(), this] {
if (!self.is_alive()) {
return;
}
pairing_tokens_count_--;
OnPairingMaybeComplete();
};
return PairingToken(std::move(unregister_cb));
}

bool Peer::BrEdrData::is_pairing() const { return pairing_tokens_count_ > 0; }

void Peer::BrEdrData::add_pairing_completion_callback(
fit::callback<void()>&& callback) {
pairing_complete_callbacks_.emplace_back(std::move(callback));
OnPairingMaybeComplete();
}

void Peer::BrEdrData::OnPairingMaybeComplete() {
if (pairing_tokens_count_ > 0 || pairing_complete_callbacks_.empty()) {
return;
}
std::vector<fit::callback<void()>> callbacks;
std::swap(callbacks, pairing_complete_callbacks_);
for (auto& cb : callbacks) {
cb();
}
}

void Peer::BrEdrData::OnConnectionStateMaybeChanged(ConnectionState previous) {
if (previous == connection_state()) {
return;
Expand Down
34 changes: 34 additions & 0 deletions pw_bluetooth_sapphire/host/gap/peer_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1608,5 +1608,39 @@ TEST_F(PeerTest, OverwritingBrEdrBondWithSameSecuritySucceeds) {
EXPECT_EQ(peer().MutBrEdr().link_key().value(), kSecureBrEdrKey2);
}

TEST_F(PeerTest, LowEnergyPairingToken) {
EXPECT_FALSE(peer().MutLe().is_pairing());
int count_0 = 0;
peer().MutLe().add_pairing_completion_callback([&count_0](){count_0++;});
EXPECT_EQ(count_0, 1);
std::optional<Peer::PairingToken> token = peer().MutLe().RegisterPairing();
int count_1 = 0;
peer().MutLe().add_pairing_completion_callback([&count_1](){count_1++;});
int count_2 = 0;
peer().MutLe().add_pairing_completion_callback([&count_2](){count_2++;});
EXPECT_EQ(count_1, 0);
EXPECT_EQ(count_2, 0);
token.reset();
EXPECT_EQ(count_1, 1);
EXPECT_EQ(count_2, 1);
}

TEST_F(PeerTest, BrEdrPairingToken) {
EXPECT_FALSE(peer().MutBrEdr().is_pairing());
int count_0 = 0;
peer().MutBrEdr().add_pairing_completion_callback([&count_0]{count_0++;});
EXPECT_EQ(count_0, 1);
std::optional<Peer::PairingToken> token = peer().MutBrEdr().RegisterPairing();
int count_1 = 0;
peer().MutBrEdr().add_pairing_completion_callback([&count_1]{count_1++;});
int count_2 = 0;
peer().MutBrEdr().add_pairing_completion_callback([&count_2]{count_2++;});
EXPECT_EQ(count_1, 0);
EXPECT_EQ(count_2, 0);
token.reset();
EXPECT_EQ(count_1, 1);
EXPECT_EQ(count_2, 1);
}

} // namespace
} // namespace bt::gap
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,9 @@ class Peer final {
// Attach peer as child node of |parent| with specified |name|.
void AttachInspect(inspect::Node& parent, std::string name = "peer");

enum class TokenType { kInitializing, kConnection };
enum class TokenType { kInitializing, kConnection, kPairing };
template <TokenType T>
class TokenWithCallback {
class [[nodiscard]] TokenWithCallback {
public:
explicit TokenWithCallback(fit::callback<void()> on_destruction)
: on_destruction_(std::move(on_destruction)) {}
Expand All @@ -170,6 +170,8 @@ class Peer final {
// update the connection state.
using ConnectionToken = TokenWithCallback<TokenType::kConnection>;

using PairingToken = TokenWithCallback<TokenType::kPairing>;

// Contains Peer data that apply only to the LE transport.
class LowEnergyData final {
public:
Expand Down Expand Up @@ -275,13 +277,25 @@ class Peer final {
// is returned that should be owned until the initialization is complete or
// canceled. The connection state may be updated and listeners may be
// notified. Multiple initializating connections may be registered.
[[nodiscard]] InitializingConnectionToken RegisterInitializingConnection();
InitializingConnectionToken RegisterInitializingConnection();

// Register a connection that is in the connected state. A token is returned
// that should be owned until the connection is disconnected. The connection
// state may be updated and listeners may be notified. Multiple connections
// may be registered.
[[nodiscard]] ConnectionToken RegisterConnection();
ConnectionToken RegisterConnection();

// Register a pairing procedure. A token is returned that should be owned
// until the pairing procedure is completed. Only one pairing may be
// registered at a time.
PairingToken RegisterPairing();

// Returns true if there are outstanding PairingTokens.
bool is_pairing() const;

// Add a callback that will be called when there are 0 outstanding
// PairingTokens (potentially immediately).
void add_pairing_completion_callback(fit::callback<void()>&& callback);

// Modify the current or preferred connection parameters.
// The device must be connectable.
Expand Down Expand Up @@ -349,6 +363,8 @@ class Peer final {
// Called when the connection state changes.
void OnConnectionStateMaybeChanged(ConnectionState previous);

void OnPairingMaybeComplete();

Peer* peer_; // weak

inspect::Node node_;
Expand Down Expand Up @@ -395,6 +411,9 @@ class Peer final {

std::optional<pw::bluetooth::emboss::LESleepClockAccuracyRange>
sleep_clock_accuracy_;

uint8_t pairing_tokens_count_ = 0;
std::vector<fit::callback<void()>> pairing_complete_callbacks_;
};

// Contains Peer data that apply only to the BR/EDR transport.
Expand Down Expand Up @@ -475,13 +494,25 @@ class Peer final {
// is returned that should be owned until the initialization is complete or
// canceled. The connection state may be updated and listeners may be
// notified. Multiple initializating connections may be registered.
[[nodiscard]] InitializingConnectionToken RegisterInitializingConnection();
InitializingConnectionToken RegisterInitializingConnection();

// Register a connection that is in the connected state. A token is returned
// that should be owned until the connection is disconnected. The connection
// state may be updated and listeners may be notified. Only one connection
// may be registered at a time (enforced by assertion).
[[nodiscard]] ConnectionToken RegisterConnection();
ConnectionToken RegisterConnection();

// Register a pairing procedure. A token is returned that should be owned
// until the pairing procedure is completed. Only one pairing may be
// registered at a time.
PairingToken RegisterPairing();

// Returns true if there are outstanding PairingTokens.
bool is_pairing() const;

// Add a callback that will be called when there are 0 outstanding
// PairingTokens (potentially immediately).
void add_pairing_completion_callback(fit::callback<void()>&& callback);

// Stores a link key resulting from Secure Simple Pairing and makes this
// peer "bonded." Marks the peer as non-temporary if necessary. All
Expand Down Expand Up @@ -509,6 +540,8 @@ class Peer final {
// Called when the connection state changes.
void OnConnectionStateMaybeChanged(ConnectionState previous);

void OnPairingMaybeComplete();

// All multi-byte fields must be in little-endian byte order as they were
// received from the controller.
void SetInquiryData(
Expand Down Expand Up @@ -537,6 +570,9 @@ class Peer final {
std::optional<sm::LTK> link_key_;

StringInspectable<std::unordered_set<UUID>> services_;

uint8_t pairing_tokens_count_ = 0;
std::vector<fit::callback<void()>> pairing_complete_callbacks_;
};

// Number that uniquely identifies this device with respect to the bt-host
Expand Down

0 comments on commit 6148e5f

Please sign in to comment.