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