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