• 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/weak_self.h"
18 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
19 #include "pw_bluetooth_sapphire/internal/host/sco/sco_connection.h"
20 #include "pw_bluetooth_sapphire/internal/host/transport/transport.h"
21 
22 namespace bt::sco {
23 
24 // ScoConnectionManager handles SCO connections for a single BR/EDR connection.
25 // This includes queuing outbound and inbound connection requests and handling
26 // events related to SCO connections.
27 class ScoConnectionManager final {
28  public:
29   // Request handle returned to clients. Cancels request when destroyed.
30   class RequestHandle final {
31    public:
RequestHandle(fit::callback<void ()> on_cancel)32     explicit RequestHandle(fit::callback<void()> on_cancel)
33         : on_cancel_(std::move(on_cancel)) {}
34     RequestHandle(RequestHandle&&) = default;
35     RequestHandle& operator=(RequestHandle&&) = default;
~RequestHandle()36     ~RequestHandle() { Cancel(); }
37 
Cancel()38     void Cancel() {
39       if (on_cancel_) {
40         on_cancel_();
41       }
42     }
43 
44    private:
45     fit::callback<void()> on_cancel_;
46     BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(RequestHandle);
47   };
48 
49   // |peer_id| corresponds to the peer associated with this BR/EDR connection.
50   // |acl_handle| corresponds to the ACL connection associated with these SCO
51   // connections. |transport| must outlive this object.
52   ScoConnectionManager(PeerId peer_id,
53                        hci_spec::ConnectionHandle acl_handle,
54                        DeviceAddress peer_address,
55                        DeviceAddress local_address,
56                        hci::Transport::WeakPtr transport);
57   // Closes connections and cancels connection requests.
58   ~ScoConnectionManager();
59 
60   // Initiate and outbound connection. A request will be queued if a connection
61   // is already in progress. On error, |callback| will be called with an error
62   // result. The error will be |kCanceled| if a connection was never attempted,
63   // or |kFailed| if establishing a connection failed. Returns a handle that
64   // will cancel the request when dropped (if connection establishment has not
65   // started).
66   using OpenConnectionResult = fit::result<HostError, ScoConnection::WeakPtr>;
67   using OpenConnectionCallback = fit::callback<void(OpenConnectionResult)>;
68   RequestHandle OpenConnection(
69       bt::StaticPacket<
70           pw::bluetooth::emboss::SynchronousConnectionParametersWriter>
71           parameters,
72       OpenConnectionCallback callback);
73 
74   // Accept inbound connection requests using the parameters given in order. The
75   // parameters will be tried in order until either a connection is successful,
76   // all parameters have been rejected, or the procedure is canceled. On
77   // success, |callback| will be called with the connection object and the index
78   // of the parameters used to establish the connection. On error, |callback|
79   // will be called with an error result. If another Open/Accept request is made
80   // before a connection request is received, this request will be canceled
81   // (with error |kCanceled|). Returns a handle that will cancel the request
82   // when destroyed (if connection establishment has not started).
83   using AcceptConnectionResult = fit::result<
84       HostError,
85       std::pair<ScoConnection::WeakPtr, size_t /*index of parameters used*/>>;
86   using AcceptConnectionCallback = fit::callback<void(AcceptConnectionResult)>;
87   RequestHandle AcceptConnection(
88       std::vector<bt::StaticPacket<
89           pw::bluetooth::emboss::SynchronousConnectionParametersWriter>>
90           parameters,
91       AcceptConnectionCallback callback);
92 
93  private:
94   using ScoRequestId = uint64_t;
95   using ConnectionResult = fit::result<
96       HostError,
97       std::pair<ScoConnection::WeakPtr, size_t /*index of parameters used*/>>;
98   using ConnectionCallback = fit::callback<void(ConnectionResult)>;
99 
100   class ConnectionRequest final {
101    public:
ConnectionRequest(ScoRequestId id_arg,bool initiator_arg,bool received_request_arg,std::vector<bt::StaticPacket<pw::bluetooth::emboss::SynchronousConnectionParametersWriter>> parameters_arg,ConnectionCallback callback_arg)102     ConnectionRequest(
103         ScoRequestId id_arg,
104         bool initiator_arg,
105         bool received_request_arg,
106         std::vector<bt::StaticPacket<
107             pw::bluetooth::emboss::SynchronousConnectionParametersWriter>>
108             parameters_arg,
109         ConnectionCallback callback_arg)
110         : id(id_arg),
111           initiator(initiator_arg),
112           received_request(received_request_arg),
113           parameters(std::move(parameters_arg)),
114           callback(std::move(callback_arg)) {}
115     ConnectionRequest(ConnectionRequest&&) = default;
116     ConnectionRequest& operator=(ConnectionRequest&&) = default;
~ConnectionRequest()117     ~ConnectionRequest() {
118       if (callback) {
119         bt_log(DEBUG, "sco", "Cancelling SCO connection request (id: %zu)", id);
120         callback(fit::error(HostError::kCanceled));
121       }
122     }
123 
124     ScoRequestId id;
125     bool initiator;
126     bool received_request;
127     size_t current_param_index = 0;
128     std::vector<bt::StaticPacket<
129         pw::bluetooth::emboss::SynchronousConnectionParametersWriter>>
130         parameters;
131     ConnectionCallback callback;
132   };
133 
134   hci::CommandChannel::EventHandlerId AddEventHandler(
135       const hci_spec::EventCode& code,
136       hci::CommandChannel::EventCallbackVariant cb);
137 
138   // Event handlers:
139   hci::CommandChannel::EventCallbackResult OnSynchronousConnectionComplete(
140       const hci::EmbossEventPacket& event);
141   hci::CommandChannel::EventCallbackResult OnConnectionRequest(
142       const hci::EmbossEventPacket& event);
143 
144   // Returns true if parameters matching the corresponding transport were found
145   // in the current request, or false otherwise. Mutates the current request's
146   // parameter index to that of the matching parameters (or past the end on
147   // failure).
148   bool FindNextParametersThatSupportSco();
149   bool FindNextParametersThatSupportEsco();
150 
151   ScoConnectionManager::RequestHandle QueueRequest(
152       bool initiator,
153       std::vector<bt::StaticPacket<
154           pw::bluetooth::emboss::SynchronousConnectionParametersWriter>>,
155       ConnectionCallback);
156 
157   void TryCreateNextConnection();
158 
159   void CompleteRequestOrTryNextParameters(ConnectionResult);
160 
161   void CompleteRequest(ConnectionResult);
162 
163   void SendCommandWithStatusCallback(
164       std::unique_ptr<hci::CommandPacket> command_packet,
165       hci::ResultFunction<> cb);
166   void SendCommandWithStatusCallback(hci::EmbossCommandPacket command_packet,
167                                      hci::ResultFunction<> cb);
168 
169   void SendRejectConnectionCommand(DeviceAddressBytes addr,
170                                    pw::bluetooth::emboss::StatusCode reason);
171 
172   // If either the queued or in progress request has the given id and can be
173   // cancelled, cancel it. Called when a RequestHandle is dropped.
174   void CancelRequestWithId(ScoRequestId);
175 
176   // The id that should be associated with the next request. Incremented when
177   // the current value is used.
178   ScoRequestId next_req_id_;
179 
180   // If a request is made while in_progress_request_ is waiting for a complete
181   // event, it gets queued in queued_request_.
182   std::optional<ConnectionRequest> queued_request_;
183 
184   std::optional<ConnectionRequest> in_progress_request_;
185 
186   // Holds active connections.
187   std::unordered_map<hci_spec::ConnectionHandle, std::unique_ptr<ScoConnection>>
188       connections_;
189 
190   // Handler IDs for registered events
191   std::vector<hci::CommandChannel::EventHandlerId> event_handler_ids_;
192 
193   PeerId peer_id_;
194 
195   const DeviceAddress local_address_;
196   const DeviceAddress peer_address_;
197 
198   hci_spec::ConnectionHandle acl_handle_;
199 
200   hci::Transport::WeakPtr transport_;
201 
202   // Keep this as the last member to make sure that all weak pointers are
203   // invalidated before other members get destroyed.
204   WeakSelf<ScoConnectionManager> weak_ptr_factory_;
205 
206   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ScoConnectionManager);
207 };
208 }  // namespace bt::sco
209