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