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 "hci/acl_manager/acl_scheduler.h"
18
19 #include <bluetooth/log.h>
20 #include <com_android_bluetooth_flags.h>
21
22 #include <deque>
23 #include <optional>
24 #include <unordered_set>
25 #include <utility>
26 #include <variant>
27
28 namespace bluetooth {
29 namespace hci {
30
31 namespace acl_manager {
32
33 struct AclCreateConnectionQueueEntry {
34 Address address;
35 common::ContextualOnceCallback<void()> callback;
36 };
37
38 struct RemoteNameRequestQueueEntry {
39 Address address;
40 common::ContextualOnceCallback<void()> callback;
41 common::ContextualOnceCallback<void()> callback_when_cancelled;
42 };
43
44 using QueueEntry = std::variant<AclCreateConnectionQueueEntry, RemoteNameRequestQueueEntry>;
45
46 struct AclScheduler::impl {
EnqueueOutgoingAclConnectionbluetooth::hci::acl_manager::AclScheduler::impl47 void EnqueueOutgoingAclConnection(Address address,
48 common::ContextualOnceCallback<void()> start_connection) {
49 pending_outgoing_operations_.push_back(
50 AclCreateConnectionQueueEntry{address, std::move(start_connection)});
51 try_dequeue_next_operation();
52 }
53
RegisterPendingIncomingConnectionbluetooth::hci::acl_manager::AclScheduler::impl54 void RegisterPendingIncomingConnection(Address address) {
55 incoming_connecting_address_set_.insert(address);
56 }
57
ReportAclConnectionCompletionbluetooth::hci::acl_manager::AclScheduler::impl58 void ReportAclConnectionCompletion(
59 Address address, common::ContextualOnceCallback<void()> handle_outgoing_connection,
60 common::ContextualOnceCallback<void()> handle_incoming_connection,
61 common::ContextualOnceCallback<void(std::string)> handle_unknown_connection) {
62 // Check if an outgoing request (a) exists, (b) is a Create Connection, (c) matches the received
63 // address
64 if (outgoing_entry_.has_value()) {
65 auto entry = std::get_if<AclCreateConnectionQueueEntry>(&outgoing_entry_.value());
66 if (entry != nullptr && entry->address == address) {
67 // If so, clear the current entry and advance the queue
68 outgoing_entry_.reset();
69 handle_outgoing_connection();
70 try_dequeue_next_operation();
71 return;
72 }
73 }
74
75 // Otherwise check if it's an incoming request and advance the queue if so
76 if (incoming_connecting_address_set_.find(address) != incoming_connecting_address_set_.end()) {
77 incoming_connecting_address_set_.erase(address);
78 handle_incoming_connection();
79 } else {
80 handle_unknown_connection(set_of_incoming_connecting_addresses());
81 }
82 try_dequeue_next_operation();
83 }
84
ReportOutgoingAclConnectionFailurebluetooth::hci::acl_manager::AclScheduler::impl85 void ReportOutgoingAclConnectionFailure() {
86 if (!outgoing_entry_.has_value()) {
87 log::error("Outgoing connection failure reported, but none present!");
88 return;
89 }
90 auto entry = std::get_if<AclCreateConnectionQueueEntry>(&outgoing_entry_.value());
91 if (entry == nullptr) {
92 log::error("Outgoing connection failure reported, but we're currently doing an RNR!");
93 return;
94 }
95 outgoing_entry_.reset();
96 try_dequeue_next_operation();
97 }
98
CancelAclConnectionbluetooth::hci::acl_manager::AclScheduler::impl99 void CancelAclConnection(Address address,
100 common::ContextualOnceCallback<void()> cancel_connection,
101 common::ContextualOnceCallback<void()> cancel_connection_completed) {
102 auto ok = cancel_outgoing_or_queued_connection(
103 [&](auto& entry) {
104 auto entry_ptr = std::get_if<AclCreateConnectionQueueEntry>(&entry);
105 return entry_ptr != nullptr && entry_ptr->address == address;
106 },
107 [&]() { cancel_connection(); },
108 [&](auto /* entry */) { cancel_connection_completed(); });
109 if (!ok) {
110 log::error("Attempted to cancel connection to {} that does not exist", address);
111 }
112 }
113
EnqueueRemoteNameRequestbluetooth::hci::acl_manager::AclScheduler::impl114 void EnqueueRemoteNameRequest(Address address,
115 common::ContextualOnceCallback<void()> start_request,
116 common::ContextualOnceCallback<void()> cancel_request_completed) {
117 pending_outgoing_operations_.push_back(RemoteNameRequestQueueEntry{
118 address, std::move(start_request), std::move(cancel_request_completed)});
119 try_dequeue_next_operation();
120 }
121
ReportRemoteNameRequestCompletionbluetooth::hci::acl_manager::AclScheduler::impl122 void ReportRemoteNameRequestCompletion(Address /* address */) {
123 if (!outgoing_entry_.has_value()) {
124 log::error("Remote name request completion reported, but none taking place!");
125 return;
126 }
127
128 std::visit(
129 [](auto&& entry) {
130 using T = std::decay_t<decltype(entry)>;
131 if constexpr (std::is_same_v<T, RemoteNameRequestQueueEntry>) {
132 log::info("Remote name request completed");
133 } else if constexpr (std::is_same_v<T, AclCreateConnectionQueueEntry>) {
134 log::error(
135 "Received RNR completion when ACL connection is outstanding - assuming the "
136 "connection has failed and continuing");
137 } else {
138 static_assert(!sizeof(T*), "non-exhaustive visitor!");
139 }
140 },
141 outgoing_entry_.value());
142
143 outgoing_entry_.reset();
144 try_dequeue_next_operation();
145 }
146
CancelRemoteNameRequestbluetooth::hci::acl_manager::AclScheduler::impl147 void CancelRemoteNameRequest(Address address,
148 common::ContextualOnceCallback<void()> cancel_request) {
149 auto ok = cancel_outgoing_or_queued_connection(
150 [&](auto& entry) {
151 auto entry_ptr = std::get_if<RemoteNameRequestQueueEntry>(&entry);
152 return entry_ptr != nullptr && entry_ptr->address == address;
153 },
154 [&]() { cancel_request(); },
155 [](auto entry) {
156 std::get<RemoteNameRequestQueueEntry>(entry).callback_when_cancelled();
157 });
158 if (!ok) {
159 log::error("Attempted to cancel remote name request to {} that does not exist", address);
160 }
161 }
162
Stopbluetooth::hci::acl_manager::AclScheduler::impl163 void Stop() { stopped_ = true; }
164
165 private:
ready_to_send_next_operationbluetooth::hci::acl_manager::AclScheduler::impl166 bool ready_to_send_next_operation() const {
167 if (stopped_) {
168 return false;
169 }
170 if (pending_outgoing_operations_.empty()) {
171 return false;
172 }
173 if (com::android::bluetooth::flags::progress_acl_scheduler_upon_incoming_connection()) {
174 if (const RemoteNameRequestQueueEntry* peek =
175 std::get_if<RemoteNameRequestQueueEntry>(&pending_outgoing_operations_.front())) {
176 if (incoming_connecting_address_set_.contains(peek->address)) {
177 log::info("Pending incoming connection and outgoing RNR to same peer:{}", peek->address);
178 return true;
179 }
180 }
181 }
182 return incoming_connecting_address_set_.empty() && !outgoing_entry_.has_value();
183 }
184
try_dequeue_next_operationbluetooth::hci::acl_manager::AclScheduler::impl185 void try_dequeue_next_operation() {
186 if (ready_to_send_next_operation()) {
187 log::info("Pending connections is not empty; so sending next connection");
188 auto entry = std::move(pending_outgoing_operations_.front());
189 pending_outgoing_operations_.pop_front();
190 std::visit([](auto&& variant) { variant.callback(); }, entry);
191 outgoing_entry_ = std::move(entry);
192 }
193 }
194
195 template <typename T, typename U, typename V>
cancel_outgoing_or_queued_connectionbluetooth::hci::acl_manager::AclScheduler::impl196 bool cancel_outgoing_or_queued_connection(T matcher, U cancel_outgoing, V cancelled_queued) {
197 // Check if relevant connection is currently outgoing
198 if (outgoing_entry_.has_value()) {
199 if (matcher(outgoing_entry_.value())) {
200 cancel_outgoing();
201 return true;
202 }
203 }
204 // Otherwise, clear from the queue
205 auto it = std::find_if(pending_outgoing_operations_.begin(), pending_outgoing_operations_.end(),
206 matcher);
207 if (it == pending_outgoing_operations_.end()) {
208 return false;
209 }
210 cancelled_queued(std::move(*it));
211 pending_outgoing_operations_.erase(it);
212 return true;
213 }
214
set_of_incoming_connecting_addressesbluetooth::hci::acl_manager::AclScheduler::impl215 const std::string set_of_incoming_connecting_addresses() const {
216 std::stringstream buffer;
217 for (const auto& c : incoming_connecting_address_set_) {
218 buffer << " " << c;
219 }
220 return buffer.str();
221 }
222
223 std::optional<QueueEntry> outgoing_entry_;
224 std::deque<QueueEntry> pending_outgoing_operations_;
225 std::unordered_set<Address> incoming_connecting_address_set_;
226 bool stopped_ = false;
227 };
228
__anon130b1bbc0902() 229 const ModuleFactory AclScheduler::Factory = ModuleFactory([]() { return new AclScheduler(); });
230
AclScheduler()231 AclScheduler::AclScheduler() : pimpl_(std::make_unique<impl>()) {}
232 AclScheduler::~AclScheduler() = default;
233
EnqueueOutgoingAclConnection(Address address,common::ContextualOnceCallback<void ()> start_connection)234 void AclScheduler::EnqueueOutgoingAclConnection(
235 Address address, common::ContextualOnceCallback<void()> start_connection) {
236 GetHandler()->Call(&impl::EnqueueOutgoingAclConnection, common::Unretained(pimpl_.get()), address,
237 std::move(start_connection));
238 }
239
RegisterPendingIncomingConnection(Address address)240 void AclScheduler::RegisterPendingIncomingConnection(Address address) {
241 GetHandler()->Call(&impl::RegisterPendingIncomingConnection, common::Unretained(pimpl_.get()),
242 address);
243 }
244
ReportAclConnectionCompletion(Address address,common::ContextualOnceCallback<void ()> handle_outgoing_connection,common::ContextualOnceCallback<void ()> handle_incoming_connection,common::ContextualOnceCallback<void (std::string)> handle_unknown_connection)245 void AclScheduler::ReportAclConnectionCompletion(
246 Address address, common::ContextualOnceCallback<void()> handle_outgoing_connection,
247 common::ContextualOnceCallback<void()> handle_incoming_connection,
248 common::ContextualOnceCallback<void(std::string)> handle_unknown_connection) {
249 GetHandler()->Call(&impl::ReportAclConnectionCompletion, common::Unretained(pimpl_.get()),
250 address, std::move(handle_outgoing_connection),
251 std::move(handle_incoming_connection), std::move(handle_unknown_connection));
252 }
253
ReportOutgoingAclConnectionFailure()254 void AclScheduler::ReportOutgoingAclConnectionFailure() {
255 GetHandler()->Call(&impl::ReportOutgoingAclConnectionFailure, common::Unretained(pimpl_.get()));
256 }
257
CancelAclConnection(Address address,common::ContextualOnceCallback<void ()> cancel_connection,common::ContextualOnceCallback<void ()> cancel_connection_completed)258 void AclScheduler::CancelAclConnection(
259 Address address, common::ContextualOnceCallback<void()> cancel_connection,
260 common::ContextualOnceCallback<void()> cancel_connection_completed) {
261 GetHandler()->Call(&impl::CancelAclConnection, common::Unretained(pimpl_.get()), address,
262 std::move(cancel_connection), std::move(cancel_connection_completed));
263 }
264
EnqueueRemoteNameRequest(Address address,common::ContextualOnceCallback<void ()> start_request,common::ContextualOnceCallback<void ()> cancel_request_completed)265 void AclScheduler::EnqueueRemoteNameRequest(
266 Address address, common::ContextualOnceCallback<void()> start_request,
267 common::ContextualOnceCallback<void()> cancel_request_completed) {
268 GetHandler()->Call(&impl::EnqueueRemoteNameRequest, common::Unretained(pimpl_.get()), address,
269 std::move(start_request), std::move(cancel_request_completed));
270 }
271
ReportRemoteNameRequestCompletion(Address address)272 void AclScheduler::ReportRemoteNameRequestCompletion(Address address) {
273 GetHandler()->Call(&impl::ReportRemoteNameRequestCompletion, common::Unretained(pimpl_.get()),
274 address);
275 }
276
CancelRemoteNameRequest(Address address,common::ContextualOnceCallback<void ()> cancel_request)277 void AclScheduler::CancelRemoteNameRequest(Address address,
278 common::ContextualOnceCallback<void()> cancel_request) {
279 GetHandler()->Call(&impl::CancelRemoteNameRequest, common::Unretained(pimpl_.get()), address,
280 std::move(cancel_request));
281 }
282
ListDependencies(ModuleList *) const283 void AclScheduler::ListDependencies(ModuleList* /* list */) const {}
284
Start()285 void AclScheduler::Start() {}
286
Stop()287 void AclScheduler::Stop() { pimpl_->Stop(); }
288
289 } // namespace acl_manager
290 } // namespace hci
291 } // namespace bluetooth
292