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 <optional>
20 #include <queue>
21 #include <unordered_set>
22 #include <variant>
23
24 namespace bluetooth {
25 namespace hci {
26
27 namespace acl_manager {
28
29 struct AclCreateConnectionQueueEntry {
30 Address address;
31 common::ContextualOnceCallback<void()> callback;
32 };
33
34 struct RemoteNameRequestQueueEntry {
35 Address address;
36 common::ContextualOnceCallback<void()> callback;
37 common::ContextualOnceCallback<void()> callback_when_cancelled;
38 };
39
40 using QueueEntry = std::variant<AclCreateConnectionQueueEntry, RemoteNameRequestQueueEntry>;
41
42 struct AclScheduler::impl {
EnqueueOutgoingAclConnectionbluetooth::hci::acl_manager::AclScheduler::impl43 void EnqueueOutgoingAclConnection(Address address, common::ContextualOnceCallback<void()> start_connection) {
44 pending_outgoing_operations_.push_back(AclCreateConnectionQueueEntry{address, std::move(start_connection)});
45 try_dequeue_next_operation();
46 }
47
RegisterPendingIncomingConnectionbluetooth::hci::acl_manager::AclScheduler::impl48 void RegisterPendingIncomingConnection(Address address) {
49 incoming_connecting_address_set_.insert(address);
50 }
51
ReportAclConnectionCompletionbluetooth::hci::acl_manager::AclScheduler::impl52 void ReportAclConnectionCompletion(
53 Address address,
54 common::ContextualOnceCallback<void()> handle_outgoing_connection,
55 common::ContextualOnceCallback<void()> handle_incoming_connection,
56 common::ContextualOnceCallback<void(std::string)> handle_unknown_connection) {
57 // Check if an outgoing request (a) exists, (b) is a Create Connection, (c) matches the received address
58 if (outgoing_entry_.has_value()) {
59 auto entry = std::get_if<AclCreateConnectionQueueEntry>(&outgoing_entry_.value());
60 if (entry != nullptr && entry->address == address) {
61 // If so, clear the current entry and advance the queue
62 outgoing_entry_.reset();
63 handle_outgoing_connection.InvokeIfNotEmpty();
64 try_dequeue_next_operation();
65 return;
66 }
67 }
68
69 // Otherwise check if it's an incoming request and advance the queue if so
70 if (incoming_connecting_address_set_.find(address) != incoming_connecting_address_set_.end()) {
71 incoming_connecting_address_set_.erase(address);
72 handle_incoming_connection.InvokeIfNotEmpty();
73 } else {
74 handle_unknown_connection.InvokeIfNotEmpty(set_of_incoming_connecting_addresses());
75 }
76 try_dequeue_next_operation();
77 }
78
ReportOutgoingAclConnectionFailurebluetooth::hci::acl_manager::AclScheduler::impl79 void ReportOutgoingAclConnectionFailure() {
80 if (!outgoing_entry_.has_value()) {
81 LOG_ERROR("Outgoing connection failure reported, but none present!");
82 return;
83 }
84 auto entry = std::get_if<AclCreateConnectionQueueEntry>(&outgoing_entry_.value());
85 if (entry == nullptr) {
86 LOG_ERROR("Outgoing connection failure reported, but we're currently doing an RNR!");
87 return;
88 }
89 outgoing_entry_.reset();
90 try_dequeue_next_operation();
91 }
92
CancelAclConnectionbluetooth::hci::acl_manager::AclScheduler::impl93 void CancelAclConnection(
94 Address address,
95 common::ContextualOnceCallback<void()> cancel_connection,
96 common::ContextualOnceCallback<void()> cancel_connection_completed) {
97 auto ok = cancel_outgoing_or_queued_connection(
98 [&](auto& entry) {
99 auto entry_ptr = std::get_if<AclCreateConnectionQueueEntry>(&entry);
100 return entry_ptr != nullptr && entry_ptr->address == address;
101 },
102 [&]() { cancel_connection.Invoke(); },
103 [&](auto entry) { cancel_connection_completed.Invoke(); });
104 if (!ok) {
105 LOG_ERROR("Attempted to cancel connection to %s that does not exist",
106 ADDRESS_TO_LOGGABLE_CSTR(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 connection has failed and "
133 "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.Invoke(); },
151 [](auto entry) { std::get<RemoteNameRequestQueueEntry>(entry).callback_when_cancelled.Invoke(); });
152 if (!ok) {
153 LOG_ERROR("Attempted to cancel remote name request "
154 "to %s that does not exist", ADDRESS_TO_LOGGABLE_CSTR(address));
155 }
156 };
157
Stopbluetooth::hci::acl_manager::AclScheduler::impl158 void Stop() {
159 stopped_ = true;
160 }
161
162 private:
try_dequeue_next_operationbluetooth::hci::acl_manager::AclScheduler::impl163 void try_dequeue_next_operation() {
164 if (stopped_) {
165 return;
166 }
167 if (incoming_connecting_address_set_.empty() && !outgoing_entry_.has_value() &&
168 !pending_outgoing_operations_.empty()) {
169 LOG_INFO("Pending connections is not empty; so sending next connection");
170 auto entry = std::move(pending_outgoing_operations_.front());
171 pending_outgoing_operations_.pop_front();
172 std::visit([](auto&& variant) { variant.callback.Invoke(); }, entry);
173 outgoing_entry_ = std::move(entry);
174 }
175 }
176
177 template <typename T, typename U, typename V>
cancel_outgoing_or_queued_connectionbluetooth::hci::acl_manager::AclScheduler::impl178 bool cancel_outgoing_or_queued_connection(T matcher, U cancel_outgoing, V cancelled_queued) {
179 // Check if relevant connection is currently outgoing
180 if (outgoing_entry_.has_value()) {
181 if (matcher(outgoing_entry_.value())) {
182 cancel_outgoing();
183 return true;
184 }
185 }
186 // Otherwise, clear from the queue
187 auto it = std::find_if(pending_outgoing_operations_.begin(), pending_outgoing_operations_.end(), matcher);
188 if (it == pending_outgoing_operations_.end()) {
189 return false;
190 }
191 cancelled_queued(std::move(*it));
192 pending_outgoing_operations_.erase(it);
193 return true;
194 }
195
set_of_incoming_connecting_addressesbluetooth::hci::acl_manager::AclScheduler::impl196 const std::string set_of_incoming_connecting_addresses() const {
197 std::stringstream buffer;
198 for (const auto& c : incoming_connecting_address_set_) buffer << " " << c;
199 return buffer.str();
200 }
201
202 std::optional<QueueEntry> outgoing_entry_;
203 std::deque<QueueEntry> pending_outgoing_operations_;
204 std::unordered_set<Address> incoming_connecting_address_set_;
205 bool stopped_ = false;
206 };
207
__anon88a7746b0902() 208 const ModuleFactory AclScheduler::Factory = ModuleFactory([]() { return new AclScheduler(); });
209
AclScheduler()210 AclScheduler::AclScheduler() : pimpl_(std::make_unique<impl>()){};
211 AclScheduler::~AclScheduler() = default;
212
EnqueueOutgoingAclConnection(Address address,common::ContextualOnceCallback<void ()> start_connection)213 void AclScheduler::EnqueueOutgoingAclConnection(
214 Address address, common::ContextualOnceCallback<void()> start_connection) {
215 GetHandler()->Call(
216 &impl::EnqueueOutgoingAclConnection, common::Unretained(pimpl_.get()), address, std::move(start_connection));
217 }
218
RegisterPendingIncomingConnection(Address address)219 void AclScheduler::RegisterPendingIncomingConnection(Address address) {
220 GetHandler()->Call(&impl::RegisterPendingIncomingConnection, common::Unretained(pimpl_.get()), address);
221 }
222
ReportAclConnectionCompletion(Address address,common::ContextualOnceCallback<void ()> handle_outgoing_connection,common::ContextualOnceCallback<void ()> handle_incoming_connection,common::ContextualOnceCallback<void (std::string)> handle_unknown_connection)223 void AclScheduler::ReportAclConnectionCompletion(
224 Address address,
225 common::ContextualOnceCallback<void()> handle_outgoing_connection,
226 common::ContextualOnceCallback<void()> handle_incoming_connection,
227 common::ContextualOnceCallback<void(std::string)> handle_unknown_connection) {
228 GetHandler()->Call(
229 &impl::ReportAclConnectionCompletion,
230 common::Unretained(pimpl_.get()),
231 address,
232 std::move(handle_outgoing_connection),
233 std::move(handle_incoming_connection),
234 std::move(handle_unknown_connection));
235 }
236
ReportOutgoingAclConnectionFailure()237 void AclScheduler::ReportOutgoingAclConnectionFailure() {
238 GetHandler()->Call(&impl::ReportOutgoingAclConnectionFailure, common::Unretained(pimpl_.get()));
239 }
240
CancelAclConnection(Address address,common::ContextualOnceCallback<void ()> cancel_connection,common::ContextualOnceCallback<void ()> cancel_connection_completed)241 void AclScheduler::CancelAclConnection(
242 Address address,
243 common::ContextualOnceCallback<void()> cancel_connection,
244 common::ContextualOnceCallback<void()> cancel_connection_completed) {
245 GetHandler()->Call(
246 &impl::CancelAclConnection,
247 common::Unretained(pimpl_.get()),
248 address,
249 std::move(cancel_connection),
250 std::move(cancel_connection_completed));
251 }
252
EnqueueRemoteNameRequest(Address address,common::ContextualOnceCallback<void ()> start_request,common::ContextualOnceCallback<void ()> cancel_request_completed)253 void AclScheduler::EnqueueRemoteNameRequest(
254 Address address,
255 common::ContextualOnceCallback<void()> start_request,
256 common::ContextualOnceCallback<void()> cancel_request_completed) {
257 GetHandler()->Call(
258 &impl::EnqueueRemoteNameRequest,
259 common::Unretained(pimpl_.get()),
260 address,
261 std::move(start_request),
262 std::move(cancel_request_completed));
263 }
264
ReportRemoteNameRequestCompletion(Address address)265 void AclScheduler::ReportRemoteNameRequestCompletion(Address address) {
266 GetHandler()->Call(&impl::ReportRemoteNameRequestCompletion, common::Unretained(pimpl_.get()), address);
267 }
268
CancelRemoteNameRequest(Address address,common::ContextualOnceCallback<void ()> cancel_request)269 void AclScheduler::CancelRemoteNameRequest(Address address, common::ContextualOnceCallback<void()> cancel_request) {
270 GetHandler()->Call(
271 &impl::CancelRemoteNameRequest, common::Unretained(pimpl_.get()), address, std::move(cancel_request));
272 }
273
ListDependencies(ModuleList * list) const274 void AclScheduler::ListDependencies(ModuleList* list) const {}
275
Start()276 void AclScheduler::Start() {}
277
Stop()278 void AclScheduler::Stop() {
279 pimpl_->Stop();
280 }
281
282 } // namespace acl_manager
283 } // namespace hci
284 } // namespace bluetooth
285