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/bredr_connection.h"
16
17 #include <utility>
18
19 namespace bt::gap {
20
21 namespace {
22
23 const char* const kInspectPeerIdPropertyName = "peer_id";
24 const char* const kInspectPairingStateNodeName = "pairing_state_manager";
25
26 } // namespace
27
BrEdrConnection(Peer::WeakPtr peer,std::unique_ptr<hci::BrEdrConnection> link,fit::closure send_auth_request_cb,fit::callback<void ()> disconnect_cb,fit::closure on_peer_disconnect_cb,l2cap::ChannelManager * l2cap,hci::Transport::WeakPtr transport,std::optional<BrEdrConnectionRequest> request,pw::async::Dispatcher & dispatcher)28 BrEdrConnection::BrEdrConnection(Peer::WeakPtr peer,
29 std::unique_ptr<hci::BrEdrConnection> link,
30 fit::closure send_auth_request_cb,
31 fit::callback<void()> disconnect_cb,
32 fit::closure on_peer_disconnect_cb,
33 l2cap::ChannelManager* l2cap,
34 hci::Transport::WeakPtr transport,
35 std::optional<BrEdrConnectionRequest> request,
36 pw::async::Dispatcher& dispatcher)
37 : peer_id_(peer->identifier()),
38 peer_(std::move(peer)),
39 link_(std::move(link)),
40 request_(std::move(request)),
41 l2cap_(l2cap),
42 sco_manager_(
43 std::make_unique<sco::ScoConnectionManager>(peer_id_,
44 link_->handle(),
45 link_->peer_address(),
46 link_->local_address(),
47 transport)),
48 interrogator_(new BrEdrInterrogator(
49 peer_, link_->handle(), transport->command_channel()->AsWeakPtr())),
50 create_time_(dispatcher.now()),
51 disconnect_cb_(std::move(disconnect_cb)),
52 peer_init_token_(request_->take_peer_init_token()),
53 peer_conn_token_(peer_->MutBrEdr().RegisterConnection()),
54 dispatcher_(dispatcher) {
55 link_->set_peer_disconnect_callback(
56 [peer_disconnect_cb = std::move(on_peer_disconnect_cb)](
57 const auto&, auto) { peer_disconnect_cb(); });
58
59 std::unique_ptr<LegacyPairingState> legacy_pairing_state = nullptr;
60 if (request_ && request_->legacy_pairing_state()) {
61 // This means that we were responding to Legacy Pairing before the
62 // ACL connection between the two devices was complete.
63 legacy_pairing_state = request_->take_legacy_pairing_state();
64
65 // TODO(fxbug.dev/356165942): Check that legacy pairing is done
66 }
67
68 pairing_state_manager_ = std::make_unique<PairingStateManager>(
69 peer_,
70 link_->GetWeakPtr(),
71 std::move(legacy_pairing_state),
72 request_ && request_->AwaitingOutgoing(),
73 std::move(send_auth_request_cb),
74 fit::bind_member<&BrEdrConnection::OnPairingStateStatus>(this));
75 }
76
~BrEdrConnection()77 BrEdrConnection::~BrEdrConnection() {
78 if (auto request = std::exchange(request_, std::nullopt);
79 request.has_value()) {
80 // Connection never completed so signal the requester(s).
81 request->NotifyCallbacks(ToResult(HostError::kNotSupported),
82 [] { return nullptr; });
83 }
84
85 sco_manager_.reset();
86 pairing_state_manager_.reset();
87 link_.reset();
88 }
89
Interrogate(BrEdrInterrogator::ResultCallback callback)90 void BrEdrConnection::Interrogate(BrEdrInterrogator::ResultCallback callback) {
91 interrogator_->Start(std::move(callback));
92 }
93
OnInterrogationComplete()94 void BrEdrConnection::OnInterrogationComplete() {
95 PW_CHECK(!interrogation_complete(),
96 "%s on a connection that's already been interrogated",
97 __FUNCTION__);
98
99 // Fulfill and clear request so that the dtor does not signal requester(s)
100 // with errors.
101 if (auto request = std::exchange(request_, std::nullopt);
102 request.has_value()) {
103 request->NotifyCallbacks(fit::ok(), [this] { return this; });
104 }
105 }
106
AddRequestCallback(BrEdrConnectionRequest::OnComplete cb)107 void BrEdrConnection::AddRequestCallback(
108 BrEdrConnectionRequest::OnComplete cb) {
109 if (!request_.has_value()) {
110 cb(fit::ok(), this);
111 return;
112 }
113
114 PW_CHECK(request_);
115 request_->AddCallback(std::move(cb));
116 }
117
CreateOrUpdatePairingState(PairingStateType type,const PairingDelegate::WeakPtr & pairing_delegate,BrEdrSecurityMode security_mode)118 void BrEdrConnection::CreateOrUpdatePairingState(
119 PairingStateType type,
120 const PairingDelegate::WeakPtr& pairing_delegate,
121 BrEdrSecurityMode security_mode) {
122 PW_CHECK(pairing_state_manager_);
123 pairing_state_manager_->CreateOrUpdatePairingState(type, pairing_delegate);
124 set_security_mode(security_mode);
125 }
126
OpenL2capChannel(l2cap::Psm psm,l2cap::ChannelParameters params,l2cap::ChannelCallback cb)127 void BrEdrConnection::OpenL2capChannel(l2cap::Psm psm,
128 l2cap::ChannelParameters params,
129 l2cap::ChannelCallback cb) {
130 if (!interrogation_complete()) {
131 // Connection is not yet ready for L2CAP; return a null channel.
132 bt_log(INFO,
133 "gap-bredr",
134 "connection not ready; canceling connect to PSM %.4x (peer: %s)",
135 psm,
136 bt_str(peer_id_));
137 cb(l2cap::Channel::WeakPtr());
138 return;
139 }
140
141 bt_log(DEBUG,
142 "gap-bredr",
143 "opening l2cap channel on psm %#.4x (peer: %s)",
144 psm,
145 bt_str(peer_id_));
146 l2cap_->OpenL2capChannel(link().handle(), psm, params, std::move(cb));
147 }
148
OpenScoConnection(bt::StaticPacket<pw::bluetooth::emboss::SynchronousConnectionParametersWriter> parameters,sco::ScoConnectionManager::OpenConnectionCallback callback)149 BrEdrConnection::ScoRequestHandle BrEdrConnection::OpenScoConnection(
150 bt::StaticPacket<
151 pw::bluetooth::emboss::SynchronousConnectionParametersWriter>
152 parameters,
153 sco::ScoConnectionManager::OpenConnectionCallback callback) {
154 return sco_manager_->OpenConnection(std::move(parameters),
155 std::move(callback));
156 }
157
AcceptScoConnection(std::vector<bt::StaticPacket<pw::bluetooth::emboss::SynchronousConnectionParametersWriter>> parameters,sco::ScoConnectionManager::AcceptConnectionCallback callback)158 BrEdrConnection::ScoRequestHandle BrEdrConnection::AcceptScoConnection(
159 std::vector<bt::StaticPacket<
160 pw::bluetooth::emboss::SynchronousConnectionParametersWriter>>
161 parameters,
162 sco::ScoConnectionManager::AcceptConnectionCallback callback) {
163 return sco_manager_->AcceptConnection(std::move(parameters),
164 std::move(callback));
165 }
166
AttachInspect(inspect::Node & parent,std::string name)167 void BrEdrConnection::AttachInspect(inspect::Node& parent, std::string name) {
168 inspect_node_ = parent.CreateChild(name);
169 inspect_properties_.peer_id = inspect_node_.CreateString(
170 kInspectPeerIdPropertyName, peer_id_.ToString());
171
172 pairing_state_manager_->AttachInspect(inspect_node_,
173 kInspectPairingStateNodeName);
174 }
175
OnPairingStateStatus(hci_spec::ConnectionHandle,hci::Result<> status)176 void BrEdrConnection::OnPairingStateStatus(hci_spec::ConnectionHandle,
177 hci::Result<> status) {
178 if (bt_is_error(
179 status,
180 DEBUG,
181 "gap-bredr",
182 "SecureSimplePairingState error status, disconnecting (peer id: %s)",
183 bt_str(peer_id_))) {
184 if (disconnect_cb_) {
185 disconnect_cb_();
186 }
187 return;
188 }
189
190 // Once pairing succeeds for the first time, the transition from Initializing
191 // -> Connected can happen.
192 peer_init_token_.reset();
193 }
194
duration() const195 pw::chrono::SystemClock::duration BrEdrConnection::duration() const {
196 return dispatcher_.now() - create_time_;
197 }
198
199 } // namespace bt::gap
200