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

Commit 6d3a7b3a authored by Rahul Arya's avatar Rahul Arya
Browse files

Pull out ACL scheduling into a separate class

There are no logical changes in this CL, it just splits out logic from
acl_manager into a reusable class.

Test: all existing tests
Bug: 246640776
Tag: #refactor
BYPASS_LONG_LINES_REASON: Bluetooth

Change-Id: I66902d20e013e83e1127dacae418b83917ea7a4c
parent e40b0073
Loading
Loading
Loading
Loading
+1 −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",
+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",
+12 −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_ = new AclScheduler();

    hci_queue_end_ = hci_layer_->GetAclQueueEnd();
    hci_queue_end_->RegisterDequeue(
@@ -67,13 +71,15 @@ 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);
    }
  }

  void Stop() {
    acl_scheduler_->Stop();

    {
      const std::lock_guard<std::mutex> lock(dumpsys_mutex_);
      delete le_impl_;
@@ -82,6 +88,9 @@ struct AclManager::impl {
      classic_impl_ = nullptr;
    }

    delete acl_scheduler_;
    acl_scheduler_ = nullptr;

    hci_queue_end_->UnregisterDequeue();
    delete round_robin_scheduler_;
    if (enqueue_registered_.exchange(false)) {
@@ -118,6 +127,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;
+147 −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 <queue>
#include <unordered_set>

namespace bluetooth {
namespace hci {

namespace acl_manager {

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

struct AclScheduler::impl {
  void EnqueueOutgoingAclConnection(Address address, common::ContextualOnceCallback<void()> start_connection) {
    pending_outgoing_connections_.push({address, std::move(start_connection)});
    try_dequeue_next_connection();
  }

  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) {
    if (outgoing_connecting_address_ == address) {
      outgoing_connecting_address_ = Address::kEmpty;
      handle_outgoing_connection.InvokeIfNotEmpty();
    } else 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_connection();
  }

  void ReportOutgoingAclConnectionFailure() {
    if (outgoing_connecting_address_ == Address::kEmpty) {
      LOG_ERROR("Outgoing connection failure reported, but none present!");
      return;
    }
    outgoing_connecting_address_ = Address::kEmpty;
    try_dequeue_next_connection();
  }

  void CancelAclConnection(
      Address address,
      common::ContextualOnceCallback<void()> cancel_connection,
      common::ContextualOnceCallback<void()> cancel_connection_completed) {
    cancel_connection.Invoke();
  }

  void Stop() {
    stopped_ = true;
  }

 private:
  void try_dequeue_next_connection() {
    if (stopped_) {
      return;
    }
    if (incoming_connecting_address_set_.empty() && outgoing_connecting_address_.IsEmpty() &&
        !pending_outgoing_connections_.empty()) {
      LOG_INFO("Pending connections is not empty; so sending next connection");
      auto entry = std::move(pending_outgoing_connections_.front());
      pending_outgoing_connections_.pop();
      outgoing_connecting_address_ = entry.address;
      entry.callback.Invoke();
    }
  }

  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();
  }

  Address outgoing_connecting_address_;
  std::queue<AclCreateConnectionQueueEntry> pending_outgoing_connections_;
  std::unordered_set<Address> incoming_connecting_address_set_;
  bool stopped_ = false;
};

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

void AclScheduler::EnqueueOutgoingAclConnection(
    Address address, common::ContextualOnceCallback<void()> start_connection) {
  pimpl_->EnqueueOutgoingAclConnection(address, std::move(start_connection));
}

void AclScheduler::RegisterPendingIncomingConnection(Address address) {
  pimpl_->RegisterPendingIncomingConnection(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) {
  pimpl_->ReportAclConnectionCompletion(
      address,
      std::move(handle_outgoing_connection),
      std::move(handle_incoming_connection),
      std::move(handle_unknown_connection));
}

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

void AclScheduler::CancelAclConnection(
    Address address,
    common::ContextualOnceCallback<void()> cancel_connection,
    common::ContextualOnceCallback<void()> cancel_connection_completed) {
  pimpl_->CancelAclConnection(address, std::move(cancel_connection), std::move(cancel_connection_completed));
}

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

}  // namespace acl_manager
}  // namespace hci
}  // namespace bluetooth
 No newline at end of file
+79 −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"

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:
  // 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);

  // Stop all queue execution. Used only prior to destruction.
  void Stop();

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

 public:
  AclScheduler();
  ~AclScheduler();
};

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