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 <lib/fit/function.h>
17 
18 #include <list>
19 
20 #include "pw_bluetooth_sapphire/internal/host/common/identifier.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/inspectable.h"
22 #include "pw_bluetooth_sapphire/internal/host/gap/legacy_pairing_state.h"
23 #include "pw_bluetooth_sapphire/internal/host/gap/peer.h"
24 #include "pw_bluetooth_sapphire/internal/host/hci/bredr_connection_request.h"
25 #include "pw_bluetooth_sapphire/internal/host/transport/error.h"
26 
27 namespace bt::gap {
28 
29 class BrEdrConnection;
30 
31 // A |BrEdrConnectionRequest| represents a request for the GAP to connect to a
32 // given |DeviceAddress| by one or more clients. BrEdrConnectionManager is
33 // responsible for tracking ConnectionRequests and passing them to the Connector
34 // when ready.
35 //
36 // There is at most One BrEdrConnectionRequest per address at any given time; if
37 // multiple clients wish to connect, they each append a callback to the list in
38 // the ConnectionRequest for the device they are interested in.
39 //
40 // If a remote peer makes an incoming request for a connection, we track that
41 // here also - whether an incoming request is pending is indicated by
42 // HasIncoming()
43 class BrEdrConnectionRequest final {
44  public:
45   using OnComplete = fit::function<void(hci::Result<>, BrEdrConnection*)>;
46   using OnTimeout = fit::function<void()>;
47   using OnFailure = fit::function<void(hci::Result<>, PeerId)>;
48   using RefFactory = fit::function<BrEdrConnection*()>;
49 
50   // Construct without a callback. Can be used for incoming only requests
51   BrEdrConnectionRequest(pw::async::Dispatcher& pw_dispatcher,
52                          const DeviceAddress& addr,
53                          PeerId peer_id,
54                          Peer::InitializingConnectionToken token);
55 
56   BrEdrConnectionRequest(pw::async::Dispatcher& pw_dispatcher,
57                          const DeviceAddress& addr,
58                          PeerId peer_id,
59                          Peer::InitializingConnectionToken token,
60                          OnComplete&& callback);
61 
62   BrEdrConnectionRequest(BrEdrConnectionRequest&&) = default;
63 
64   // Creates a hci::BrEdrConnectionRequest from this gap::BrEdrConnectionRequest
65   std::unique_ptr<hci::BrEdrConnectionRequest> CreateHciConnectionRequest(
66       hci::CommandChannel* command_channel,
67       std::optional<uint16_t> clock_offset,
68       std::optional<pw::bluetooth::emboss::PageScanRepetitionMode>
69           page_scan_repetition_mode,
70       OnTimeout timeout_cb,
71       OnFailure failure_cb,
72       pw::async::Dispatcher& dispatcher);
73 
74   bool ShouldRetry(hci::Error failure_mode);
75 
AddCallback(OnComplete cb)76   void AddCallback(OnComplete cb) {
77     callbacks_.Mutable()->push_back(std::move(cb));
78   }
79 
80   // Notifies all elements in |callbacks| with |status| and the result of
81   // |generate_ref|. Called by the appropriate manager once a connection request
82   // has completed, successfully or otherwise
83   void NotifyCallbacks(hci::Result<> status, const RefFactory& generate_ref);
84 
BeginIncoming()85   void BeginIncoming() { has_incoming_.Set(true); }
CompleteIncoming()86   void CompleteIncoming() { has_incoming_.Set(false); }
HasIncoming()87   bool HasIncoming() const { return *has_incoming_; }
AwaitingOutgoing()88   bool AwaitingOutgoing() { return !callbacks_->empty(); }
89 
90   // Attach request inspect node as a child of |parent| named |name|.
91   void AttachInspect(inspect::Node& parent, std::string name);
92 
address()93   DeviceAddress address() const { return address_; }
94 
95   // If a role change occurs while this request is still pending, set it here so
96   // that the correct role is used when connection establishment completes.
set_role_change(pw::bluetooth::emboss::ConnectionRole role)97   void set_role_change(pw::bluetooth::emboss::ConnectionRole role) {
98     role_change_ = role;
99   }
100 
101   // If the default role of the requested connection is changed during
102   // connection establishment, the new role will be returned.
role_change()103   const std::optional<pw::bluetooth::emboss::ConnectionRole>& role_change()
104       const {
105     return role_change_;
106   }
107 
set_legacy_pairing_state(std::unique_ptr<LegacyPairingState> legacy_pairing_state)108   void set_legacy_pairing_state(
109       std::unique_ptr<LegacyPairingState> legacy_pairing_state) {
110     legacy_pairing_state_ = std::move(legacy_pairing_state);
111   }
legacy_pairing_state()112   LegacyPairingState* legacy_pairing_state() {
113     return legacy_pairing_state_.get();
114   }
take_legacy_pairing_state()115   std::unique_ptr<LegacyPairingState> take_legacy_pairing_state() {
116     return std::move(legacy_pairing_state_);
117   }
118 
take_peer_init_token()119   Peer::InitializingConnectionToken take_peer_init_token() {
120     PW_CHECK(peer_init_conn_token_);
121     return std::exchange(peer_init_conn_token_, std::nullopt).value();
122   }
123 
124  private:
125   PeerId peer_id_;
126   DeviceAddress address_;
127   UintInspectable<std::list<OnComplete>> callbacks_;
128   BoolInspectable<bool> has_incoming_{false};
129   std::optional<pw::bluetooth::emboss::ConnectionRole> role_change_;
130   std::unique_ptr<LegacyPairingState> legacy_pairing_state_;
131 
132   // Used to determine whether an outbound connection request should be retried.
133   // If empty, no HCI Create Connection Requests associated with this object
134   // have been made, otherwise stores the time at which the first HCI request
135   // associated with this object was made.
136   IntInspectable<std::optional<pw::chrono::SystemClock::time_point>>
137       first_create_connection_req_made_{
138           std::nullopt,
139           [](auto& t) { return t ? t->time_since_epoch().count() : -1; }};
140 
141   inspect::StringProperty peer_id_property_;
142   inspect::Node inspect_node_;
143 
144   std::optional<Peer::InitializingConnectionToken> peer_init_conn_token_;
145 
146   // Time after which a connection attempt is considered to have timed out.
147   pw::chrono::SystemClock::duration request_timeout_{
148       kBrEdrCreateConnectionTimeout};
149 
150   pw::async::Dispatcher& dispatcher_;
151 
152   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(BrEdrConnectionRequest);
153 };
154 
155 }  // namespace bt::gap
156