• 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 #pragma once
16 #include "pw_bluetooth_sapphire/internal/host/common/identifier.h"
17 #include "pw_bluetooth_sapphire/internal/host/common/inspectable.h"
18 #include "pw_bluetooth_sapphire/internal/host/gap/gap.h"
19 #include "pw_bluetooth_sapphire/internal/host/gap/generic_access_client.h"
20 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_connection_handle.h"
21 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_connection_request.h"
22 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt.h"
23 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
24 #include "pw_bluetooth_sapphire/internal/host/hci/low_energy_connection.h"
25 #include "pw_bluetooth_sapphire/internal/host/l2cap/channel_manager.h"
26 #include "pw_bluetooth_sapphire/internal/host/sm/delegate.h"
27 #include "pw_bluetooth_sapphire/internal/host/sm/security_manager.h"
28 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
29 #include "pw_bluetooth_sapphire/internal/host/transport/command_channel.h"
30 
31 namespace bt::gap {
32 
33 namespace internal {
34 
35 // LowEnergyConnector constructs LowEnergyConnection instances immediately upon
36 // successful completion of the link layer connection procedure (to hook up HCI
37 // event callbacks). However, LowEnergyConnections aren't exposed to the rest of
38 // the stack (including the LowEnergyConnectionManager) until fully
39 // interrogated, as completion of the link-layer connection process is
40 // insufficient to guarantee a working connection. Thus this class represents
41 // the state of an active *AND* (outside of LowEnergyConnector) known-functional
42 // connection.
43 //
44 // Instances are kept alive as long as there is at least one
45 // LowEnergyConnectionHandle that references them. Instances are expected to be
46 // destroyed immediately after a peer disconnect event is received (as indicated
47 // by peer_disconnect_cb).
48 class LowEnergyConnection final : public sm::Delegate {
49  public:
50   // |peer| is the peer that this connection is connected to.
51   // |link| is the underlying LE HCI connection that this connection corresponds
52   // to. |peer_disconnect_cb| will be called when the peer disconnects. It will
53   // not be called before this method returns. |error_cb| will be called when a
54   // fatal connection error occurs and the connection should be closed (e.g.
55   // when L2CAP reports an error). It will not be called before this method
56   // returns. |conn_mgr| is the LowEnergyConnectionManager that owns this
57   // connection. |l2cap|, |gatt|, and |cmd_channel| are pointers to the
58   // interfaces of the corresponding layers. Returns nullptr if connection
59   // initialization fails.
60   using PeerDisconnectCallback =
61       fit::callback<void(pw::bluetooth::emboss::StatusCode)>;
62   using ErrorCallback = fit::callback<void()>;
63   static std::unique_ptr<LowEnergyConnection> Create(
64       Peer::WeakPtr peer,
65       std::unique_ptr<hci::LowEnergyConnection> link,
66       LowEnergyConnectionOptions connection_options,
67       PeerDisconnectCallback peer_disconnect_cb,
68       ErrorCallback error_cb,
69       WeakSelf<LowEnergyConnectionManager>::WeakPtr conn_mgr,
70       l2cap::ChannelManager* l2cap,
71       gatt::GATT::WeakPtr gatt,
72       hci::CommandChannel::WeakPtr cmd_channel,
73       pw::async::Dispatcher& dispatcher);
74 
75   // Notifies request callbacks and connection refs of the disconnection.
76   ~LowEnergyConnection() override;
77 
78   // Create a reference to this connection. When the last reference is dropped,
79   // this connection will be disconnected.
80   std::unique_ptr<LowEnergyConnectionHandle> AddRef();
81 
82   // Decrements the ref count. Must be called when a LowEnergyConnectionHandle
83   // is released/destroyed.
84   void DropRef(LowEnergyConnectionHandle* ref);
85 
86   // Used to respond to protocol/service requests for increased security.
87   void OnSecurityRequest(sm::SecurityLevel level, sm::ResultFunction<> cb);
88 
89   // Handles a pairing request (i.e. security upgrade) received from "higher
90   // levels", likely initiated from GAP. This will only be used by pairing
91   // requests that are initiated in the context of testing. May only be called
92   // on an already-established connection.
93   void UpgradeSecurity(sm::SecurityLevel level,
94                        sm::BondableMode bondable_mode,
95                        sm::ResultFunction<> cb);
96 
97   // Cancels any on-going pairing procedures and sets up SMP to use the provided
98   // new I/O capabilities for future pairing procedures.
99   void ResetSecurityManager(sm::IOCapability ioc);
100 
101   // Must be called when interrogation has completed. May update connection
102   // parameters if all initialization procedures have completed.
103   void OnInterrogationComplete();
104 
105   // Attach connection as child node of |parent| with specified |name|.
106   void AttachInspect(inspect::Node& parent, std::string name);
107 
set_security_mode(LESecurityMode mode)108   void set_security_mode(LESecurityMode mode) {
109     BT_ASSERT(sm_);
110     sm_->set_security_mode(mode);
111   }
112 
113   // Sets a callback that will be called when the peer disconnects.
set_peer_disconnect_callback(PeerDisconnectCallback cb)114   void set_peer_disconnect_callback(PeerDisconnectCallback cb) {
115     BT_ASSERT(cb);
116     peer_disconnect_callback_ = std::move(cb);
117   }
118 
119   // |peer_conn_token| is a token generated by the connected Peer, and is used
120   // to synchronize connection state.
set_peer_conn_token(Peer::ConnectionToken peer_conn_token)121   void set_peer_conn_token(Peer::ConnectionToken peer_conn_token) {
122     BT_ASSERT(interrogation_completed_);
123     BT_ASSERT(!peer_conn_token_);
124     peer_conn_token_ = std::move(peer_conn_token);
125   }
126 
127   // Sets a callback that will be called when a fatal connection error occurs.
set_error_callback(ErrorCallback cb)128   void set_error_callback(ErrorCallback cb) {
129     BT_ASSERT(cb);
130     error_callback_ = std::move(cb);
131   }
132 
ref_count()133   size_t ref_count() const { return refs_->size(); }
134 
peer_id()135   PeerId peer_id() const { return peer_->identifier(); }
handle()136   hci_spec::ConnectionHandle handle() const { return link_->handle(); }
link()137   hci::LowEnergyConnection* link() const { return link_.get(); }
bondable_mode()138   sm::BondableMode bondable_mode() const {
139     BT_ASSERT(sm_);
140     return sm_->bondable_mode();
141   }
142 
security()143   sm::SecurityProperties security() const {
144     BT_ASSERT(sm_);
145     return sm_->security();
146   }
147 
148   using WeakPtr = WeakSelf<LowEnergyConnection>::WeakPtr;
GetWeakPtr()149   LowEnergyConnection::WeakPtr GetWeakPtr() { return weak_self_.GetWeakPtr(); }
150 
151  private:
152   LowEnergyConnection(Peer::WeakPtr peer,
153                       std::unique_ptr<hci::LowEnergyConnection> link,
154                       LowEnergyConnectionOptions connection_options,
155                       PeerDisconnectCallback peer_disconnect_cb,
156                       ErrorCallback error_cb,
157                       WeakSelf<LowEnergyConnectionManager>::WeakPtr conn_mgr,
158                       l2cap::ChannelManager* l2cap,
159                       gatt::GATT::WeakPtr gatt,
160                       hci::CommandChannel::WeakPtr cmd_channel,
161                       pw::async::Dispatcher& dispatcher);
162 
163   // Registers this connection with L2CAP and initializes the fixed channel
164   // protocols. Return true on success, false on failure.
165   [[nodiscard]] bool InitializeFixedChannels();
166 
167   // Register handlers for HCI events that correspond to this connection.
168   void RegisterEventHandlers();
169 
170   // Start kLEConnectionPauseCentral/Peripheral timeout that will update
171   // connection parameters. Should be called as soon as this GAP connection is
172   // established.
173   void StartConnectionPauseTimeout();
174 
175   // Start kLEConnectionPausePeripheral timeout that will send a connection
176   // parameter update request. Should be called as soon as connection is
177   // established.
178   void StartConnectionPausePeripheralTimeout();
179 
180   // Start kLEConnectionPauseCentral timeout that will update connection
181   // parameters. Should be called as soon as connection is established.
182   void StartConnectionPauseCentralTimeout();
183 
184   // Initializes SecurityManager and GATT.
185   // Called by the L2CAP layer once the link has been registered and the fixed
186   // channels have been opened. Returns false if GATT initialization fails.
187   [[nodiscard]] bool OnL2capFixedChannelsOpened(
188       l2cap::Channel::WeakPtr att,
189       l2cap::Channel::WeakPtr smp,
190       LowEnergyConnectionOptions connection_options);
191 
192   // Called when the preferred connection parameters have been received for a LE
193   // peripheral. This can happen in the form of:
194   //
195   //   1. <<Peripheral Connection Interval Range>> advertising data field
196   //   2. "Peripheral Preferred Connection Parameters" GATT characteristic
197   //      (under "GAP" service)
198   //   3. HCI LE Remote Connection Parameter Request Event
199   //   4. L2CAP Connection Parameter Update request
200   //
201   // TODO(fxbug.dev/42147867): Support #1 above.
202   // TODO(fxbug.dev/42147868): Support #3 above.
203   //
204   // This method caches |params| for later connection attempts and sends the
205   // parameters to the controller if the initializing procedures are complete
206   // (since we use more agressing initial parameters for pairing and service
207   // discovery, as recommended by the specification in v5.0, Vol 3, Part C,
208   // Section 9.3.12.1).
209   //
210   // |peer_id| uniquely identifies the peer. |handle| represents
211   // the logical link that |params| should be applied to.
212   void OnNewLEConnectionParams(
213       const hci_spec::LEPreferredConnectionParameters& params);
214 
215   // As an LE peripheral, request that the connection parameters |params| be
216   // used on the given connection |conn| with peer |peer_id|. This may send an
217   // HCI LE Connection Update command or an L2CAP Connection Parameter Update
218   // Request depending on what the local and remote controllers support.
219   //
220   // Interrogation must have completed before this may be called.
221   void RequestConnectionParameterUpdate(
222       const hci_spec::LEPreferredConnectionParameters& params);
223 
224   // Handler for connection parameter update command sent when an update is
225   // requested by RequestConnectionParameterUpdate.
226   //
227   // If the HCI LE Connection Update command fails with status
228   // kUnsupportedRemoteFeature, the update will be retried with an L2CAP
229   // Connection Parameter Update Request.
230   void HandleRequestConnectionParameterUpdateCommandStatus(
231       hci_spec::LEPreferredConnectionParameters params, hci::Result<> status);
232 
233   // As an LE peripheral, send an L2CAP Connection Parameter Update Request
234   // requesting |params| on the LE signaling channel of the given logical link
235   // |handle|.
236   //
237   // NOTE: This should only be used if the LE peripheral and/or LE central do
238   // not support the Connection Parameters Request Link Layer Control Procedure
239   // (Core Spec v5.2  Vol 3, Part A, Sec 4.20). If they do,
240   // UpdateConnectionParams(...) should be used instead.
241   void L2capRequestConnectionParameterUpdate(
242       const hci_spec::LEPreferredConnectionParameters& params);
243 
244   // Requests that the controller use the given connection |params| by sending
245   // an HCI LE Connection Update command. This may be issued on both the LE
246   // peripheral and the LE central.
247   //
248   // The link layer may modify the preferred parameters |params| before
249   // initiating the Connection Parameters Request Link Layer Control Procedure
250   // (Core Spec v5.2, Vol 6, Part B, Sec 5.1.7).
251   //
252   // If non-null, |status_cb| will be called when the HCI Command Status event
253   // is received.
254   //
255   // The HCI LE Connection Update Complete event will be generated after the
256   // parameters have been applied or if the update fails, and will indicate the
257   // (possibly modified) parameter values.
258   //
259   // NOTE: If the local host is an LE peripheral, then the local controller and
260   // the remote LE central must have indicated support for this procedure in the
261   // LE feature mask. Otherwise, L2capRequestConnectionParameterUpdate(...)
262   // should be used instead.
263   using StatusCallback = hci::ResultCallback<>;
264   void UpdateConnectionParams(
265       const hci_spec::LEPreferredConnectionParameters& params,
266       StatusCallback status_cb = nullptr);
267 
268   // This event may be generated without host interaction by the Link Layer, or
269   // as the result of a Connection Update Command sent by either device, which
270   // is why it is not simply handled by the command handler. (See Core Spec
271   // v5.2, Vol 6, Part B, Sec 5.1.7.1).
272   void OnLEConnectionUpdateComplete(const hci::EmbossEventPacket& event);
273 
274   // Updates or requests an update of the connection parameters, for central and
275   // peripheral roles respectively, if interrogation has completed.
276   // TODO(fxbug.dev/42159733): Wait to update connection parameters until all
277   // initialization procedures have completed.
278   void MaybeUpdateConnectionParameters();
279 
280   // Registers the peer with GATT and initiates service discovery. If
281   // |service_uuid| is specified, only discover the indicated service and the
282   // GAP service. Returns true on success, false on failure.
283   bool InitializeGatt(l2cap::Channel::WeakPtr att,
284                       std::optional<UUID> service_uuid);
285 
286   // Called when service discovery completes. |services| will only include
287   // services with the GAP UUID (there should only be one, but this is not
288   // guaranteed).
289   void OnGattServicesResult(att::Result<> status, gatt::ServiceList services);
290 
291   // Notifies all connection refs of disconnection.
292   void CloseRefs();
293 
294   // sm::Delegate overrides:
295   void OnNewPairingData(const sm::PairingData& pairing_data) override;
296   void OnPairingComplete(sm::Result<> status) override;
297   void OnAuthenticationFailure(hci::Result<> status) override;
298   void OnNewSecurityProperties(const sm::SecurityProperties& sec) override;
299   std::optional<sm::IdentityInfo> OnIdentityInformationRequest() override;
300   void ConfirmPairing(ConfirmCallback confirm) override;
301   void DisplayPasskey(uint32_t passkey,
302                       sm::Delegate::DisplayMethod method,
303                       ConfirmCallback confirm) override;
304   void RequestPasskey(PasskeyResponseCallback respond) override;
305 
306   pw::async::Dispatcher& dispatcher_;
307 
308   // Notifies Peer of connection destruction. This should be ordered first so
309   // that it is destroyed last.
310   std::optional<Peer::ConnectionToken> peer_conn_token_;
311 
312   Peer::WeakPtr peer_;
313   std::unique_ptr<hci::LowEnergyConnection> link_;
314   LowEnergyConnectionOptions connection_options_;
315   WeakSelf<LowEnergyConnectionManager>::WeakPtr conn_mgr_;
316 
317   struct InspectProperties {
318     inspect::StringProperty peer_id;
319     inspect::StringProperty peer_address;
320   };
321   InspectProperties inspect_properties_;
322   inspect::Node inspect_node_;
323 
324   // Used to update the L2CAP layer to reflect the correct link security level.
325   l2cap::ChannelManager* l2cap_;
326 
327   // Reference to the GATT profile layer is used to initiate service discovery
328   // and register the link.
329   gatt::GATT::WeakPtr gatt_;
330 
331   // The ATT Bearer is owned by LowEnergyConnection but weak pointers are passed
332   // to the GATT layer. As such, this connection must be unregistered from the
333   // GATT layer before the Bearer is destroyed. Created during initialization,
334   // but if initialization fails this may be nullptr.
335   std::unique_ptr<att::Bearer> att_bearer_;
336 
337   // SMP pairing manager.
338   std::unique_ptr<sm::SecurityManager> sm_;
339 
340   hci::CommandChannel::WeakPtr cmd_;
341 
342   // Called when the peer disconnects.
343   PeerDisconnectCallback peer_disconnect_callback_;
344 
345   // Called when a fatal connection error occurs and the connection should be
346   // closed (e.g. when L2CAP reports an error).
347   ErrorCallback error_callback_;
348 
349   // Event handler ID for the HCI LE Connection Update Complete event.
350   hci::CommandChannel::EventHandlerId conn_update_cmpl_handler_id_;
351 
352   // Called with the status of the next HCI LE Connection Update Complete event.
353   // The HCI LE Connection Update command does not have its own complete event
354   // handler because the HCI LE Connection Complete event can be generated for
355   // other reasons.
356   fit::callback<void(pw::bluetooth::emboss::StatusCode)>
357       le_conn_update_complete_command_callback_;
358 
359   // Called after kLEConnectionPausePeripheral.
360   std::optional<SmartTask> conn_pause_peripheral_timeout_;
361 
362   // Called after kLEConnectionPauseCentral.
363   std::optional<SmartTask> conn_pause_central_timeout_;
364 
365   // Set to true when a request to update the connection parameters has been
366   // sent.
367   bool connection_parameters_update_requested_ = false;
368 
369   bool interrogation_completed_ = false;
370 
371   // LowEnergyConnectionManager is responsible for making sure that these
372   // pointers are always valid.
373   using ConnectionHandleSet = std::unordered_set<LowEnergyConnectionHandle*>;
374   IntInspectable<ConnectionHandleSet> refs_;
375 
376   // Null until service discovery completes.
377   std::optional<GenericAccessClient> gap_service_client_;
378 
379   WeakSelf<LowEnergyConnection> weak_self_;
380   WeakSelf<sm::Delegate> weak_delegate_;
381 
382   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyConnection);
383 };
384 
385 }  // namespace internal
386 }  // namespace bt::gap
387