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/hci/acl_connection.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/transport/transport.h"
18
19 namespace bt::hci {
20
21 namespace {
22
23 template <CommandChannel::EventCallbackResult (
24 AclConnection::* EventHandlerMethod)(const EventPacket&)>
BindEventHandler(const WeakSelf<AclConnection>::WeakPtr & conn)25 CommandChannel::EventCallback BindEventHandler(
26 const WeakSelf<AclConnection>::WeakPtr& conn) {
27 return [conn](const EventPacket& event) {
28 if (conn.is_alive()) {
29 return (conn.get().*EventHandlerMethod)(event);
30 }
31 return CommandChannel::EventCallbackResult::kRemove;
32 };
33 }
34
35 template <CommandChannel::EventCallbackResult (
36 AclConnection::* EventHandlerMethod)(const EmbossEventPacket&)>
BindEventHandler(const WeakPtr<AclConnection> & conn)37 CommandChannel::EmbossEventCallback BindEventHandler(
38 const WeakPtr<AclConnection>& conn) {
39 return [conn](const EmbossEventPacket& event) {
40 if (conn.is_alive()) {
41 return (conn.get().*EventHandlerMethod)(event);
42 }
43 return CommandChannel::EventCallbackResult::kRemove;
44 };
45 }
46
47 } // namespace
48
AclConnection(hci_spec::ConnectionHandle handle,const DeviceAddress & local_address,const DeviceAddress & peer_address,pw::bluetooth::emboss::ConnectionRole role,const Transport::WeakPtr & hci)49 AclConnection::AclConnection(hci_spec::ConnectionHandle handle,
50 const DeviceAddress& local_address,
51 const DeviceAddress& peer_address,
52 pw::bluetooth::emboss::ConnectionRole role,
53 const Transport::WeakPtr& hci)
54 : Connection(handle,
55 local_address,
56 peer_address,
57 hci,
58 [handle, hci] {
59 AclConnection::OnDisconnectionComplete(handle, hci);
60 }),
61 role_(role),
62 weak_self_(this) {
63 auto self = weak_self_.GetWeakPtr();
64 enc_change_id_ = hci->command_channel()->AddEventHandler(
65 hci_spec::kEncryptionChangeEventCode,
66 BindEventHandler<&AclConnection::OnEncryptionChangeEvent>(self));
67 enc_key_refresh_cmpl_id_ = hci->command_channel()->AddEventHandler(
68 hci_spec::kEncryptionKeyRefreshCompleteEventCode,
69 BindEventHandler<&AclConnection::OnEncryptionKeyRefreshCompleteEvent>(
70 self));
71 }
72
~AclConnection()73 AclConnection::~AclConnection() {
74 // Unregister HCI event handlers.
75 hci()->command_channel()->RemoveEventHandler(enc_change_id_);
76 hci()->command_channel()->RemoveEventHandler(enc_key_refresh_cmpl_id_);
77 }
78
OnDisconnectionComplete(hci_spec::ConnectionHandle handle,const Transport::WeakPtr & hci)79 void AclConnection::OnDisconnectionComplete(hci_spec::ConnectionHandle handle,
80 const Transport::WeakPtr& hci) {
81 if (!hci.is_alive()) {
82 return;
83 }
84 // Notify ACL data channel that packets have been flushed from controller
85 // buffer.
86 hci->acl_data_channel()->ClearControllerPacketCount(handle);
87 }
88
OnEncryptionChangeEvent(const EmbossEventPacket & event)89 CommandChannel::EventCallbackResult AclConnection::OnEncryptionChangeEvent(
90 const EmbossEventPacket& event) {
91 BT_ASSERT(event.event_code() == hci_spec::kEncryptionChangeEventCode);
92
93 auto params =
94 event
95 .unchecked_view<pw::bluetooth::emboss::EncryptionChangeEventV1View>();
96 if (!params.Ok()) {
97 bt_log(WARN, "hci", "malformed encryption change event");
98 return CommandChannel::EventCallbackResult::kContinue;
99 }
100
101 hci_spec::ConnectionHandle handle = params.connection_handle().Read();
102
103 // Silently ignore the event as it isn't meant for this connection.
104 if (handle != this->handle()) {
105 return CommandChannel::EventCallbackResult::kContinue;
106 }
107
108 if (state() != Connection::State::kConnected) {
109 bt_log(DEBUG, "hci", "encryption change ignored for closed connection");
110 return CommandChannel::EventCallbackResult::kContinue;
111 }
112
113 Result<> result = event.ToResult();
114 encryption_status_ = params.encryption_enabled().Read();
115 bool encryption_enabled =
116 encryption_status_ != pw::bluetooth::emboss::EncryptionStatus::OFF;
117
118 bt_log(DEBUG,
119 "hci",
120 "encryption change (%s) %s",
121 encryption_enabled ? "enabled" : "disabled",
122 bt_str(result));
123
124 // If peer and local Secure Connections support are present, the pairing logic
125 // needs to verify that the status received in the Encryption Changed event is
126 // for AES encryption.
127 if (use_secure_connections_ &&
128 encryption_status_ !=
129 pw::bluetooth::emboss::EncryptionStatus::ON_WITH_AES_FOR_BREDR) {
130 bt_log(DEBUG,
131 "hci",
132 "BR/EDR Secure Connection must use AES encryption. Closing "
133 "connection...");
134 HandleEncryptionStatus(fit::error(Error(HostError::kInsufficientSecurity)),
135 /*key_refreshed=*/false);
136 return CommandChannel::EventCallbackResult::kContinue;
137 }
138
139 HandleEncryptionStatus(result.is_ok()
140 ? Result<bool>(fit::ok(encryption_enabled))
141 : result.take_error(),
142 /*key_refreshed=*/false);
143 return CommandChannel::EventCallbackResult::kContinue;
144 }
145
146 CommandChannel::EventCallbackResult
OnEncryptionKeyRefreshCompleteEvent(const EmbossEventPacket & event)147 AclConnection::OnEncryptionKeyRefreshCompleteEvent(
148 const EmbossEventPacket& event) {
149 const auto params =
150 event
151 .view<pw::bluetooth::emboss::EncryptionKeyRefreshCompleteEventView>();
152 const hci_spec::ConnectionHandle handle = params.connection_handle().Read();
153
154 // Silently ignore this event as it isn't meant for this connection.
155 if (handle != this->handle()) {
156 return CommandChannel::EventCallbackResult::kContinue;
157 }
158
159 if (state() != Connection::State::kConnected) {
160 bt_log(
161 DEBUG, "hci", "encryption key refresh ignored for closed connection");
162 return CommandChannel::EventCallbackResult::kContinue;
163 }
164
165 Result<> status = event.ToResult();
166 bt_log(DEBUG, "hci", "encryption key refresh %s", bt_str(status));
167
168 // Report that encryption got disabled on failure status. The accuracy of this
169 // isn't that important since the link will be disconnected.
170 HandleEncryptionStatus(status.is_ok()
171 ? Result<bool>(fit::ok(/*enabled=*/true))
172 : status.take_error(),
173 /*key_refreshed=*/true);
174
175 return CommandChannel::EventCallbackResult::kContinue;
176 }
177
178 } // namespace bt::hci
179