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