1 /*
2 * Copyright 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "acl_scheduler.h"
18
19 #include <bluetooth/log.h>
20
21 #include <optional>
22 #include <unordered_set>
23 #include <variant>
24
25 namespace bluetooth {
26 namespace hci {
27
28 namespace acl_manager {
29
30 struct AclCreateConnectionQueueEntry {
31 Address address;
32 common::ContextualOnceCallback<void()> callback;
33 };
34
35 struct RemoteNameRequestQueueEntry {
36 Address address;
37 common::ContextualOnceCallback<void()> callback;
38 common::ContextualOnceCallback<void()> callback_when_cancelled;
39 };
40
41 using QueueEntry = std::variant<AclCreateConnectionQueueEntry, RemoteNameRequestQueueEntry>;
42
43 struct AclScheduler::impl {
EnqueueOutgoingAclConnectionbluetooth::hci::acl_manager::AclScheduler::impl44 void EnqueueOutgoingAclConnection(Address address, common::ContextualOnceCallback<void()> start_connection) {
45 pending_outgoing_operations_.push_back(AclCreateConnectionQueueEntry{address, std::move(start_connection)});
46 try_dequeue_next_operation();
47 }
48
RegisterPendingIncomingConnectionbluetooth::hci::acl_manager::AclScheduler::impl49 void RegisterPendingIncomingConnection(Address address) {
50 incoming_connecting_address_set_.insert(address);
51 }
52
ReportAclConnectionCompletionbluetooth::hci::acl_manager::AclScheduler::impl53 void ReportAclConnectionCompletion(
54 Address address,
55 common::ContextualOnceCallback<void()> handle_outgoing_connection,
56 common::ContextualOnceCallback<void()> handle_incoming_connection,
57 common::ContextualOnceCallback<void(std::string)> handle_unknown_connection) {
58 // Check if an outgoing request (a) exists, (b) is a Create Connection, (c) matches the received address
59 if (outgoing_entry_.has_value()) {
60 auto entry = std::get_if<AclCreateConnectionQueueEntry>(&outgoing_entry_.value());
61 if (entry != nullptr && entry->address == address) {
62 // If so, clear the current entry and advance the queue
63 outgoing_entry_.reset();
64 handle_outgoing_connection();
65 try_dequeue_next_operation();
66 return;
67 }
68 }
69
70 // Otherwise check if it's an incoming request and advance the queue if so
71 if (incoming_connecting_address_set_.find(address) != incoming_connecting_address_set_.end()) {
72 incoming_connecting_address_set_.erase(address);
73 handle_incoming_connection();
74 } else {
75 handle_unknown_connection(set_of_incoming_connecting_addresses());
76 }
77 try_dequeue_next_operation();
78 }
79
ReportOutgoingAclConnectionFailurebluetooth::hci::acl_manager::AclScheduler::impl80 void ReportOutgoingAclConnectionFailure() {
81 if (!outgoing_entry_.has_value()) {
82 log::error("Outgoing connection failure reported, but none present!");
83 return;
84 }
85 auto entry = std::get_if<AclCreateConnectionQueueEntry>(&outgoing_entry_.value());
86 if (entry == nullptr) {
87 log::error("Outgoing connection failure reported, but we're currently doing an RNR!");
88 return;
89 }
90 outgoing_entry_.reset();
91 try_dequeue_next_operation();
92 }
93
CancelAclConnectionbluetooth::hci::acl_manager::AclScheduler::impl94 void CancelAclConnection(
95 Address address,
96 common::ContextualOnceCallback<void()> cancel_connection,
97 common::ContextualOnceCallback<void()> cancel_connection_completed) {
98 auto ok = cancel_outgoing_or_queued_connection(
99 [&](auto& entry) {
100 auto entry_ptr = std::get_if<AclCreateConnectionQueueEntry>(&entry);
101 return entry_ptr != nullptr && entry_ptr->address == address;
102 },
103 [&]() { cancel_connection(); },
104 [&](auto /* entry */) { cancel_connection_completed(); });
105 if (!ok) {
106 log::error("Attempted to cancel connection to {} that does not exist", address);
107 }
108 }
109
EnqueueRemoteNameRequestbluetooth::hci::acl_manager::AclScheduler::impl110 void EnqueueRemoteNameRequest(
111 Address address,
112 common::ContextualOnceCallback<void()> start_request,
113 common::ContextualOnceCallback<void()> cancel_request_completed) {
114 pending_outgoing_operations_.push_back(
115 RemoteNameRequestQueueEntry{address, std::move(start_request), std::move(cancel_request_completed)});
116 try_dequeue_next_operation();
117 }
118
ReportRemoteNameRequestCompletionbluetooth::hci::acl_manager::AclScheduler::impl119 void ReportRemoteNameRequestCompletion(Address /* address */) {
120 if (!outgoing_entry_.has_value()) {
121 log::error("Remote name request completion reported, but none taking place!");
122 return;
123 }
124
125 std::visit(
126 [](auto&& entry) {
127 using T = std::decay_t<decltype(entry)>;
128 if constexpr (std::is_same_v<T, RemoteNameRequestQueueEntry>) {
129 log::info("Remote name request completed");
130 } else if constexpr (std::is_same_v<T, AclCreateConnectionQueueEntry>) {
131 log::error(
132 "Received RNR completion when ACL connection is outstanding - assuming the "
133 "connection has failed and continuing");
134 } else {
135 static_assert(!sizeof(T*), "non-exhaustive visitor!");
136 }
137 },
138 outgoing_entry_.value());
139
140 outgoing_entry_.reset();
141 try_dequeue_next_operation();
142 }
143
CancelRemoteNameRequestbluetooth::hci::acl_manager::AclScheduler::impl144 void CancelRemoteNameRequest(Address address, common::ContextualOnceCallback<void()> cancel_request) {
145 auto ok = cancel_outgoing_or_queued_connection(
146 [&](auto& entry) {
147 auto entry_ptr = std::get_if<RemoteNameRequestQueueEntry>(&entry);
148 return entry_ptr != nullptr && entry_ptr->address == address;
149 },
150 [&]() { cancel_request(); },
151 [](auto entry) { std::get<RemoteNameRequestQueueEntry>(entry).callback_when_cancelled(); });
152 if (!ok) {
153 log::error("Attempted to cancel remote name request to {} that does not exist", address);
154 }
155 };
156
Stopbluetooth::hci::acl_manager::AclScheduler::impl157 void Stop() {
158 stopped_ = true;
159 }
160
161 private:
try_dequeue_next_operationbluetooth::hci::acl_manager::AclScheduler::impl162 void try_dequeue_next_operation() {
163 if (stopped_) {
164 return;
165 }
166 if (incoming_connecting_address_set_.empty() && !outgoing_entry_.has_value() &&
167 !pending_outgoing_operations_.empty()) {
168 log::info("Pending connections is not empty; so sending next connection");
169 auto entry = std::move(pending_outgoing_operations_.front());
170 pending_outgoing_operations_.pop_front();
171 std::visit([](auto&& variant) { variant.callback(); }, entry);
172 outgoing_entry_ = std::move(entry);
173 }
174 }
175
176 template <typename T, typename U, typename V>
cancel_outgoing_or_queued_connectionbluetooth::hci::acl_manager::AclScheduler::impl177 bool cancel_outgoing_or_queued_connection(T matcher, U cancel_outgoing, V cancelled_queued) {
178 // Check if relevant connection is currently outgoing
179 if (outgoing_entry_.has_value()) {
180 if (matcher(outgoing_entry_.value())) {
181 cancel_outgoing();
182 return true;
183 }
184 }
185 // Otherwise, clear from the queue
186 auto it = std::find_if(pending_outgoing_operations_.begin(), pending_outgoing_operations_.end(), matcher);
187 if (it == pending_outgoing_operations_.end()) {
188 return false;
189 }
190 cancelled_queued(std::move(*it));
191 pending_outgoing_operations_.erase(it);
192 return true;
193 }
194
set_of_incoming_connecting_addressesbluetooth::hci::acl_manager::AclScheduler::impl195 const std::string set_of_incoming_connecting_addresses() const {
196 std::stringstream buffer;
197 for (const auto& c : incoming_connecting_address_set_) buffer << " " << c;
198 return buffer.str();
199 }
200
201 std::optional<QueueEntry> outgoing_entry_;
202 std::deque<QueueEntry> pending_outgoing_operations_;
203 std::unordered_set<Address> incoming_connecting_address_set_;
204 bool stopped_ = false;
205 };
206
__anon99d21ec60902() 207 const ModuleFactory AclScheduler::Factory = ModuleFactory([]() { return new AclScheduler(); });
208
AclScheduler()209 AclScheduler::AclScheduler() : pimpl_(std::make_unique<impl>()){};
210 AclScheduler::~AclScheduler() = default;
211
EnqueueOutgoingAclConnection(Address address,common::ContextualOnceCallback<void ()> start_connection)212 void AclScheduler::EnqueueOutgoingAclConnection(
213 Address address, common::ContextualOnceCallback<void()> start_connection) {
214 GetHandler()->Call(
215 &impl::EnqueueOutgoingAclConnection, common::Unretained(pimpl_.get()), address, std::move(start_connection));
216 }
217
RegisterPendingIncomingConnection(Address address)218 void AclScheduler::RegisterPendingIncomingConnection(Address address) {
219 GetHandler()->Call(&impl::RegisterPendingIncomingConnection, common::Unretained(pimpl_.get()), address);
220 }
221
ReportAclConnectionCompletion(Address address,common::ContextualOnceCallback<void ()> handle_outgoing_connection,common::ContextualOnceCallback<void ()> handle_incoming_connection,common::ContextualOnceCallback<void (std::string)> handle_unknown_connection)222 void AclScheduler::ReportAclConnectionCompletion(
223 Address address,
224 common::ContextualOnceCallback<void()> handle_outgoing_connection,
225 common::ContextualOnceCallback<void()> handle_incoming_connection,
226 common::ContextualOnceCallback<void(std::string)> handle_unknown_connection) {
227 GetHandler()->Call(
228 &impl::ReportAclConnectionCompletion,
229 common::Unretained(pimpl_.get()),
230 address,
231 std::move(handle_outgoing_connection),
232 std::move(handle_incoming_connection),
233 std::move(handle_unknown_connection));
234 }
235
ReportOutgoingAclConnectionFailure()236 void AclScheduler::ReportOutgoingAclConnectionFailure() {
237 GetHandler()->Call(&impl::ReportOutgoingAclConnectionFailure, common::Unretained(pimpl_.get()));
238 }
239
CancelAclConnection(Address address,common::ContextualOnceCallback<void ()> cancel_connection,common::ContextualOnceCallback<void ()> cancel_connection_completed)240 void AclScheduler::CancelAclConnection(
241 Address address,
242 common::ContextualOnceCallback<void()> cancel_connection,
243 common::ContextualOnceCallback<void()> cancel_connection_completed) {
244 GetHandler()->Call(
245 &impl::CancelAclConnection,
246 common::Unretained(pimpl_.get()),
247 address,
248 std::move(cancel_connection),
249 std::move(cancel_connection_completed));
250 }
251
EnqueueRemoteNameRequest(Address address,common::ContextualOnceCallback<void ()> start_request,common::ContextualOnceCallback<void ()> cancel_request_completed)252 void AclScheduler::EnqueueRemoteNameRequest(
253 Address address,
254 common::ContextualOnceCallback<void()> start_request,
255 common::ContextualOnceCallback<void()> cancel_request_completed) {
256 GetHandler()->Call(
257 &impl::EnqueueRemoteNameRequest,
258 common::Unretained(pimpl_.get()),
259 address,
260 std::move(start_request),
261 std::move(cancel_request_completed));
262 }
263
ReportRemoteNameRequestCompletion(Address address)264 void AclScheduler::ReportRemoteNameRequestCompletion(Address address) {
265 GetHandler()->Call(&impl::ReportRemoteNameRequestCompletion, common::Unretained(pimpl_.get()), address);
266 }
267
CancelRemoteNameRequest(Address address,common::ContextualOnceCallback<void ()> cancel_request)268 void AclScheduler::CancelRemoteNameRequest(Address address, common::ContextualOnceCallback<void()> cancel_request) {
269 GetHandler()->Call(
270 &impl::CancelRemoteNameRequest, common::Unretained(pimpl_.get()), address, std::move(cancel_request));
271 }
272
ListDependencies(ModuleList *) const273 void AclScheduler::ListDependencies(ModuleList* /* list */) const {}
274
Start()275 void AclScheduler::Start() {}
276
Stop()277 void AclScheduler::Stop() {
278 pimpl_->Stop();
279 }
280
281 } // namespace acl_manager
282 } // namespace hci
283 } // namespace bluetooth
284