• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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