• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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