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