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