Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a2290b48 authored by Rahul Arya's avatar Rahul Arya Committed by Gerrit Code Review
Browse files

Merge changes from topic "stop-failing-my-presubmits"

* changes:
  Add Remote Name Request scheduling to ACL scheduler
  Fix ACL Create Connection Cancel
  GD-ify ACLScheduler
  Pull out ACL scheduling into a separate class
parents acbd65a3 caec024c
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ filegroup {
    name: "BluetoothHciSources",
    srcs: [
        "acl_manager/acl_connection.cc",
        "acl_manager/acl_scheduler.cc",
        "acl_manager/classic_acl_connection.cc",
        "acl_manager/le_acl_connection.cc",
        "acl_manager/round_robin_scheduler.cc",
@@ -37,6 +38,7 @@ filegroup {
        "acl_manager/classic_acl_connection_test.cc",
        "acl_builder_test.cc",
        "acl_manager_unittest.cc",
        "acl_manager/acl_scheduler_test.cc",
        "address_unittest.cc",
        "address_with_type_test.cc",
        "class_of_device_unittest.cc",
+1 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ source_set("BluetoothHciSources") {
  sources = [
    "acl_manager.cc",
    "acl_manager/acl_connection.cc",
    "acl_manager/acl_scheduler.cc",
    "acl_manager/acl_fragmenter.cc",
    "acl_manager/classic_acl_connection.cc",
    "acl_manager/le_acl_connection.cc",
+9 −2
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <set>

#include "common/bidi_queue.h"
#include "hci/acl_manager/acl_scheduler.h"
#include "hci/acl_manager/classic_impl.h"
#include "hci/acl_manager/connection_management_callbacks.h"
#include "hci/acl_manager/le_acl_connection.h"
@@ -52,6 +53,8 @@ using acl_manager::LeConnectionCallbacks;

using acl_manager::RoundRobinScheduler;

using acl_manager::AclScheduler;

struct AclManager::impl {
  impl(const AclManager& acl_manager) : acl_manager_(acl_manager) {}

@@ -60,6 +63,7 @@ struct AclManager::impl {
    handler_ = acl_manager_.GetHandler();
    controller_ = acl_manager_.GetDependency<Controller>();
    round_robin_scheduler_ = new RoundRobinScheduler(handler_, controller_, hci_layer_->GetAclQueueEnd());
    acl_scheduler_ = acl_manager_.GetDependency<AclScheduler>();

    hci_queue_end_ = hci_layer_->GetAclQueueEnd();
    hci_queue_end_->RegisterDequeue(
@@ -67,8 +71,8 @@ struct AclManager::impl {
    bool crash_on_unknown_handle = false;
    {
      const std::lock_guard<std::mutex> lock(dumpsys_mutex_);
      classic_impl_ =
          new classic_impl(hci_layer_, controller_, handler_, round_robin_scheduler_, crash_on_unknown_handle);
      classic_impl_ = new classic_impl(
          hci_layer_, controller_, handler_, round_robin_scheduler_, crash_on_unknown_handle, acl_scheduler_);
      le_impl_ = new le_impl(hci_layer_, controller_, handler_, round_robin_scheduler_, crash_on_unknown_handle);
    }
  }
@@ -90,6 +94,7 @@ struct AclManager::impl {
    hci_queue_end_ = nullptr;
    handler_ = nullptr;
    hci_layer_ = nullptr;
    acl_scheduler_ = nullptr;
  }

  // Invoked from some external Queue Reactable context 2
@@ -118,6 +123,7 @@ struct AclManager::impl {

  classic_impl* classic_impl_ = nullptr;
  le_impl* le_impl_ = nullptr;
  AclScheduler* acl_scheduler_ = nullptr;
  os::Handler* handler_ = nullptr;
  Controller* controller_ = nullptr;
  HciLayer* hci_layer_ = nullptr;
@@ -315,6 +321,7 @@ void AclManager::ListDependencies(ModuleList* list) const {
  list->add<HciLayer>();
  list->add<Controller>();
  list->add<storage::StorageModule>();
  list->add<AclScheduler>();
}

void AclManager::Start() {
+282 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "acl_scheduler.h"

#include <optional>
#include <queue>
#include <unordered_set>
#include <variant>

namespace bluetooth {
namespace hci {

namespace acl_manager {

struct AclCreateConnectionQueueEntry {
  Address address;
  common::ContextualOnceCallback<void()> callback;
};

struct RemoteNameRequestQueueEntry {
  Address address;
  common::ContextualOnceCallback<void()> callback;
  common::ContextualOnceCallback<void()> callback_when_cancelled;
};

using QueueEntry = std::variant<AclCreateConnectionQueueEntry, RemoteNameRequestQueueEntry>;

struct AclScheduler::impl {
  void EnqueueOutgoingAclConnection(Address address, common::ContextualOnceCallback<void()> start_connection) {
    pending_outgoing_operations_.push_back(AclCreateConnectionQueueEntry{address, std::move(start_connection)});
    try_dequeue_next_operation();
  }

  void RegisterPendingIncomingConnection(Address address) {
    incoming_connecting_address_set_.insert(address);
  }

  void ReportAclConnectionCompletion(
      Address address,
      common::ContextualOnceCallback<void()> handle_outgoing_connection,
      common::ContextualOnceCallback<void()> handle_incoming_connection,
      common::ContextualOnceCallback<void(std::string)> handle_unknown_connection) {
    // Check if an outgoing request (a) exists, (b) is a Create Connection, (c) matches the received address
    if (outgoing_entry_.has_value()) {
      auto entry = std::get_if<AclCreateConnectionQueueEntry>(&outgoing_entry_.value());
      if (entry != nullptr && entry->address == address) {
        // If so, clear the current entry and advance the queue
        outgoing_entry_.reset();
        handle_outgoing_connection.InvokeIfNotEmpty();
        try_dequeue_next_operation();
        return;
      }
    }

    // Otherwise check if it's an incoming request and advance the queue if so
    if (incoming_connecting_address_set_.find(address) != incoming_connecting_address_set_.end()) {
      incoming_connecting_address_set_.erase(address);
      handle_incoming_connection.InvokeIfNotEmpty();
    } else {
      handle_unknown_connection.InvokeIfNotEmpty(set_of_incoming_connecting_addresses());
    }
    try_dequeue_next_operation();
  }

  void ReportOutgoingAclConnectionFailure() {
    if (!outgoing_entry_.has_value()) {
      LOG_ERROR("Outgoing connection failure reported, but none present!");
      return;
    }
    auto entry = std::get_if<AclCreateConnectionQueueEntry>(&outgoing_entry_.value());
    if (entry == nullptr) {
      LOG_ERROR("Outgoing connection failure reported, but we're currently doing an RNR!");
      return;
    }
    outgoing_entry_.reset();
    try_dequeue_next_operation();
  }

  void CancelAclConnection(
      Address address,
      common::ContextualOnceCallback<void()> cancel_connection,
      common::ContextualOnceCallback<void()> cancel_connection_completed) {
    auto ok = cancel_outgoing_or_queued_connection(
        [&](auto& entry) {
          auto entry_ptr = std::get_if<AclCreateConnectionQueueEntry>(&entry);
          return entry_ptr != nullptr && entry_ptr->address == address;
        },
        [&]() { cancel_connection.Invoke(); },
        [&](auto entry) { cancel_connection_completed.Invoke(); });
    if (!ok) {
      LOG_ERROR("Attempted to cancel connection to %s that does not exist", address.ToString().c_str());
    }
  }

  void EnqueueRemoteNameRequest(
      Address address,
      common::ContextualOnceCallback<void()> start_request,
      common::ContextualOnceCallback<void()> cancel_request_completed) {
    pending_outgoing_operations_.push_back(
        RemoteNameRequestQueueEntry{address, std::move(start_request), std::move(cancel_request_completed)});
    try_dequeue_next_operation();
  }

  void ReportRemoteNameRequestCompletion(Address address) {
    if (!outgoing_entry_.has_value()) {
      LOG_ERROR("Remote name request completion reported, but none taking place!");
      return;
    }

    std::visit(
        [](auto&& entry) {
          using T = std::decay_t<decltype(entry)>;
          if constexpr (std::is_same_v<T, RemoteNameRequestQueueEntry>) {
            LOG_INFO("Remote name request completed");
          } else if constexpr (std::is_same_v<T, AclCreateConnectionQueueEntry>) {
            LOG_ERROR(
                "Received RNR completion when ACL connection is outstanding - assuming the connection has failed and "
                "continuing");
          } else {
            static_assert(!sizeof(T*), "non-exhaustive visitor!");
          }
        },
        outgoing_entry_.value());

    outgoing_entry_.reset();
    try_dequeue_next_operation();
  }

  void CancelRemoteNameRequest(Address address, common::ContextualOnceCallback<void()> cancel_request) {
    auto ok = cancel_outgoing_or_queued_connection(
        [&](auto& entry) {
          auto entry_ptr = std::get_if<RemoteNameRequestQueueEntry>(&entry);
          return entry_ptr != nullptr && entry_ptr->address == address;
        },
        [&]() { cancel_request.Invoke(); },
        [](auto entry) { std::get<RemoteNameRequestQueueEntry>(entry).callback_when_cancelled.Invoke(); });
    if (!ok) {
      LOG_ERROR("Attempted to cancel remote name request to %s that does not exist", address.ToString().c_str());
    }
  };

  void Stop() {
    stopped_ = true;
  }

 private:
  void try_dequeue_next_operation() {
    if (stopped_) {
      return;
    }
    if (incoming_connecting_address_set_.empty() && !outgoing_entry_.has_value() &&
        !pending_outgoing_operations_.empty()) {
      LOG_INFO("Pending connections is not empty; so sending next connection");
      auto entry = std::move(pending_outgoing_operations_.front());
      pending_outgoing_operations_.pop_front();
      std::visit([](auto&& variant) { variant.callback.Invoke(); }, entry);
      outgoing_entry_ = std::move(entry);
    }
  }

  template <typename T, typename U, typename V>
  bool cancel_outgoing_or_queued_connection(T matcher, U cancel_outgoing, V cancelled_queued) {
    // Check if relevant connection is currently outgoing
    if (outgoing_entry_.has_value()) {
      if (matcher(outgoing_entry_.value())) {
        cancel_outgoing();
        return true;
      }
    }
    // Otherwise, clear from the queue
    auto it = std::find_if(pending_outgoing_operations_.begin(), pending_outgoing_operations_.end(), matcher);
    if (it == pending_outgoing_operations_.end()) {
      return false;
    }
    cancelled_queued(std::move(*it));
    pending_outgoing_operations_.erase(it);
    return true;
  }

  const std::string set_of_incoming_connecting_addresses() const {
    std::stringstream buffer;
    for (const auto& c : incoming_connecting_address_set_) buffer << " " << c;
    return buffer.str();
  }

  std::optional<QueueEntry> outgoing_entry_;
  std::deque<QueueEntry> pending_outgoing_operations_;
  std::unordered_set<Address> incoming_connecting_address_set_;
  bool stopped_ = false;
};

const ModuleFactory AclScheduler::Factory = ModuleFactory([]() { return new AclScheduler(); });

AclScheduler::AclScheduler() : pimpl_(std::make_unique<impl>()){};
AclScheduler::~AclScheduler() = default;

void AclScheduler::EnqueueOutgoingAclConnection(
    Address address, common::ContextualOnceCallback<void()> start_connection) {
  GetHandler()->Call(
      &impl::EnqueueOutgoingAclConnection, common::Unretained(pimpl_.get()), address, std::move(start_connection));
}

void AclScheduler::RegisterPendingIncomingConnection(Address address) {
  GetHandler()->Call(&impl::RegisterPendingIncomingConnection, common::Unretained(pimpl_.get()), address);
}

void AclScheduler::ReportAclConnectionCompletion(
    Address address,
    common::ContextualOnceCallback<void()> handle_outgoing_connection,
    common::ContextualOnceCallback<void()> handle_incoming_connection,
    common::ContextualOnceCallback<void(std::string)> handle_unknown_connection) {
  GetHandler()->Call(
      &impl::ReportAclConnectionCompletion,
      common::Unretained(pimpl_.get()),
      address,
      std::move(handle_outgoing_connection),
      std::move(handle_incoming_connection),
      std::move(handle_unknown_connection));
}

void AclScheduler::ReportOutgoingAclConnectionFailure() {
  GetHandler()->Call(&impl::ReportOutgoingAclConnectionFailure, common::Unretained(pimpl_.get()));
}

void AclScheduler::CancelAclConnection(
    Address address,
    common::ContextualOnceCallback<void()> cancel_connection,
    common::ContextualOnceCallback<void()> cancel_connection_completed) {
  GetHandler()->Call(
      &impl::CancelAclConnection,
      common::Unretained(pimpl_.get()),
      address,
      std::move(cancel_connection),
      std::move(cancel_connection_completed));
}

void AclScheduler::EnqueueRemoteNameRequest(
    Address address,
    common::ContextualOnceCallback<void()> start_request,
    common::ContextualOnceCallback<void()> cancel_request_completed) {
  GetHandler()->Call(
      &impl::EnqueueRemoteNameRequest,
      common::Unretained(pimpl_.get()),
      address,
      std::move(start_request),
      std::move(cancel_request_completed));
}

void AclScheduler::ReportRemoteNameRequestCompletion(Address address) {
  GetHandler()->Call(&impl::ReportRemoteNameRequestCompletion, common::Unretained(pimpl_.get()), address);
}

void AclScheduler::CancelRemoteNameRequest(Address address, common::ContextualOnceCallback<void()> cancel_request) {
  GetHandler()->Call(
      &impl::CancelRemoteNameRequest, common::Unretained(pimpl_.get()), address, std::move(cancel_request));
}

void AclScheduler::ListDependencies(ModuleList* list) const {}

void AclScheduler::Start() {}

void AclScheduler::Stop() {
  pimpl_->Stop();
}

}  // namespace acl_manager
}  // namespace hci
}  // namespace bluetooth
 No newline at end of file
+101 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <memory>
#include <string>
#include <utility>

#include "common/contextual_callback.h"
#include "hci/hci_packets.h"
#include "module.h"

namespace bluetooth {
namespace hci {
namespace acl_manager {

// The AclScheduler is responsible for *scheduling* ACL connection-related operations (outgoing connections,
// incoming connections, and remote name requests). It maintains a queue of operations initiated by us, and tracks
// all incoming connections. We should never initiate a connection operation directly - instead, it should always
// pass through this class, so that we can be sure that it does not conflict with other operations.
//
// However, it does not perform any actual HCI operations itself - it simply takes in callbacks, and executes them
// at the appropriate time.
class AclScheduler : public bluetooth::Module {
 public:
  // Schedule an ACL Create Connection request
  void EnqueueOutgoingAclConnection(Address address, common::ContextualOnceCallback<void()> start_connection);

  // Inform the scheduler that we are handling an incoming connection. This will block all future outgoing ACL
  // connection events until the incoming connection is deregistered.
  void RegisterPendingIncomingConnection(Address address);

  // Report that an ACL connection has completed, and dispatch to the appropriate callback based on the internal
  // state. Then, start the next operation.
  void ReportAclConnectionCompletion(
      Address address,
      common::ContextualOnceCallback<void()> handle_outgoing_connection,
      common::ContextualOnceCallback<void()> handle_incoming_connection,
      common::ContextualOnceCallback<void(std::string)> handle_unknown_connection);

  // Same as above, but for the outgoing ACL connection in particular (and no callbacks)
  void ReportOutgoingAclConnectionFailure();

  // Cancel an ACL connection. If the request is already outgoing, we will invoke cancel_connection, without clearing
  // the outgoing request. Otherwise, we will remove the request from the queue, invoke cancel_connection_completed,
  // and execute the next request in the queue.
  void CancelAclConnection(
      Address address,
      common::ContextualOnceCallback<void()> cancel_connection,
      common::ContextualOnceCallback<void()> cancel_connection_completed);

  // Schedule a Remote Name Request. When the request is started, start_request will be invoked. If the request is
  // cancelled before it is dequeued, cancel_request_completed will be invoked.
  void EnqueueRemoteNameRequest(
      Address address,
      common::ContextualOnceCallback<void()> start_request,
      common::ContextualOnceCallback<void()> cancel_request_completed);

  // Report that a Remote Name Request connection has completed, so we can resume popping from the queue.
  void ReportRemoteNameRequestCompletion(Address address);

  // Cancel an Remote Name Request. If the request is already outgoing, we will invoke cancel_request, without
  // clearing the outgoing request. Otherwise, we will invoke the cancel_request_completed callback registered on
  // the initial enqueue.
  void CancelRemoteNameRequest(Address address, common::ContextualOnceCallback<void()> cancel_request);

 private:
  struct impl;
  std::unique_ptr<impl> pimpl_;

 protected:
  void ListDependencies(ModuleList* list) const override;
  void Start() override;
  void Stop() override;
  std::string ToString() const override {
    return std::string("AclSchedulerModule");
  }

 public:
  static const ModuleFactory Factory;
  AclScheduler();
  ~AclScheduler();
};

}  // namespace acl_manager
}  // namespace hci
}  // namespace bluetooth
 No newline at end of file
Loading