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 #include "pw_bluetooth_sapphire/internal/host/gap/bredr_connection_request.h"
16
17 namespace bt::gap {
18
19 namespace {
20
21 const char* const kInspectHasIncomingPropertyName = "has_incoming";
22 const char* const kInspectCallbacksPropertyName = "callbacks";
23 const char* const kInspectFirstCreateConnectionReqMadeName =
24 "first_create_connection_request_timestamp";
25 const char* const kInspectPeerIdPropertyName = "peer_id";
26 constexpr pw::chrono::SystemClock::duration kRetryWindowAfterFirstCreateConn =
27 std::chrono::seconds(30);
28
29 } // namespace
30
BrEdrConnectionRequest(pw::async::Dispatcher & pw_dispatcher,const DeviceAddress & addr,PeerId peer_id,Peer::InitializingConnectionToken token)31 BrEdrConnectionRequest::BrEdrConnectionRequest(
32 pw::async::Dispatcher& pw_dispatcher,
33 const DeviceAddress& addr,
34 PeerId peer_id,
35 Peer::InitializingConnectionToken token)
36 : peer_id_(peer_id),
37 address_(addr),
38 callbacks_(/*convert=*/[](auto& c) { return c.size(); }),
39 peer_init_conn_token_(std::move(token)),
40 dispatcher_(pw_dispatcher) {}
41
BrEdrConnectionRequest(pw::async::Dispatcher & pw_dispatcher,const DeviceAddress & addr,PeerId peer_id,Peer::InitializingConnectionToken token,OnComplete && callback)42 BrEdrConnectionRequest::BrEdrConnectionRequest(
43 pw::async::Dispatcher& pw_dispatcher,
44 const DeviceAddress& addr,
45 PeerId peer_id,
46 Peer::InitializingConnectionToken token,
47 OnComplete&& callback)
48 : BrEdrConnectionRequest(pw_dispatcher, addr, peer_id, std::move(token)) {
49 callbacks_.Mutable()->push_back(std::move(callback));
50 }
51
NotifyCallbacks(hci::Result<> status,const RefFactory & generate_ref)52 void BrEdrConnectionRequest::NotifyCallbacks(hci::Result<> status,
53 const RefFactory& generate_ref) {
54 // Clear token before notifying callbacks so that connection state change is
55 // reflected in callbacks.
56 peer_init_conn_token_.reset();
57
58 // If this request has been moved from, |callbacks_| may be empty.
59 for (const auto& callback : *callbacks_) {
60 callback(status, generate_ref());
61 }
62 }
63
AttachInspect(inspect::Node & parent,std::string name)64 void BrEdrConnectionRequest::AttachInspect(inspect::Node& parent,
65 std::string name) {
66 inspect_node_ = parent.CreateChild(name);
67 has_incoming_.AttachInspect(inspect_node_, kInspectHasIncomingPropertyName);
68 callbacks_.AttachInspect(inspect_node_, kInspectCallbacksPropertyName);
69 first_create_connection_req_made_.AttachInspect(
70 inspect_node_, kInspectFirstCreateConnectionReqMadeName);
71 peer_id_property_ = inspect_node_.CreateString(kInspectPeerIdPropertyName,
72 peer_id_.ToString());
73 }
74
75 std::unique_ptr<hci::BrEdrConnectionRequest>
CreateHciConnectionRequest(hci::CommandChannel * command_channel,std::optional<uint16_t> clock_offset,std::optional<pw::bluetooth::emboss::PageScanRepetitionMode> page_scan_repetition_mode,OnTimeout timeout_cb,OnFailure failure_cb,pw::async::Dispatcher & dispatcher)76 BrEdrConnectionRequest::CreateHciConnectionRequest(
77 hci::CommandChannel* command_channel,
78 std::optional<uint16_t> clock_offset,
79 std::optional<pw::bluetooth::emboss::PageScanRepetitionMode>
80 page_scan_repetition_mode,
81 OnTimeout timeout_cb,
82 OnFailure failure_cb,
83 pw::async::Dispatcher& dispatcher) {
84 std::unique_ptr<hci::BrEdrConnectionRequest> request =
85 std::make_unique<hci::BrEdrConnectionRequest>(
86 peer_id_, address_, std::move(timeout_cb), dispatcher);
87
88 request->CreateConnection(command_channel,
89 clock_offset,
90 page_scan_repetition_mode,
91 request_timeout_,
92 std::move(failure_cb));
93
94 // Record that the first create connection attempt was made (if not already
95 // attempted) for the outstanding HCI connection request for peer with id
96 // |peer_id_|
97 if (!first_create_connection_req_made_.value()) {
98 first_create_connection_req_made_.Set(dispatcher_.now());
99 }
100
101 return request;
102 }
103
ShouldRetry(hci::Error failure_mode)104 bool BrEdrConnectionRequest::ShouldRetry(hci::Error failure_mode) {
105 pw::chrono::SystemClock::time_point now = dispatcher_.now();
106 std::optional<pw::chrono::SystemClock::time_point>
107 first_create_conn_req_made = first_create_connection_req_made_.value();
108 return failure_mode.is(pw::bluetooth::emboss::StatusCode::PAGE_TIMEOUT) &&
109 first_create_conn_req_made.has_value() &&
110 now - *first_create_conn_req_made < kRetryWindowAfterFirstCreateConn;
111 }
112 } // namespace bt::gap
113