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

Commit d1344a55 authored by Rahul Arya's avatar Rahul Arya
Browse files

Simplify handling of connection_complete

Reduce the possibility of accidentally failing to unblock the queue, by
splitting the callback into one function that handles connection
creation, and one that manages the state machine.

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

Change-Id: Id7aa897ede4ef9abeb26e60eedc1bb8e8953e2d2
parent 5d1027e5
Loading
Loading
Loading
Loading
+73 −61
Original line number Diff line number Diff line
@@ -286,67 +286,6 @@ struct classic_impl : public security::ISecurityManagerListener {
    dequeue_next_connection();
  }

  void on_connection_complete(EventView packet) {
    ConnectionCompleteView connection_complete = ConnectionCompleteView::Create(packet);
    ASSERT(connection_complete.IsValid());
    auto status = connection_complete.GetStatus();
    auto address = connection_complete.GetBdAddr();
    if (client_callbacks_ == nullptr) {
      LOG_WARN("No client callbacks registered for connection");
      return;
    }
    Role current_role = Role::CENTRAL;
    bool locally_initiated = true;
    if (outgoing_connecting_address_ == address) {
      outgoing_connecting_address_ = Address::kEmpty;
    } else {
      auto incoming_address = incoming_connecting_address_set_.find(address);
      if (incoming_address == incoming_connecting_address_set_.end()) {
        ASSERT_LOG(
            status != ErrorCode::UNKNOWN_CONNECTION,
            "No prior connection request for %s expecting:%s",
            address.ToString().c_str(),
            set_of_incoming_connecting_addresses().c_str());
        LOG_WARN("No matching connection to %s (%s)", address.ToString().c_str(), ErrorCodeText(status).c_str());
        LOG_WARN("Firmware error after RemoteNameRequestCancel?");
        return;
      }
      incoming_connecting_address_set_.erase(incoming_address);
      current_role = Role::PERIPHERAL;
      locally_initiated = false;
    }
    if (status != ErrorCode::SUCCESS) {
      client_handler_->Post(common::BindOnce(&ConnectionCallbacks::OnConnectFail, common::Unretained(client_callbacks_),
                                             address, status));
    } else {
      uint16_t handle = connection_complete.GetConnectionHandle();
      auto queue = std::make_shared<AclConnection::Queue>(10);
      auto queue_down_end = queue->GetDownEnd();
      round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue);
      std::unique_ptr<ClassicAclConnection> connection(
          new ClassicAclConnection(std::move(queue), acl_connection_interface_, handle, address));
      connection->locally_initiated_ = locally_initiated;
      connections.add(
          handle,
          AddressWithType{address, AddressType::PUBLIC_DEVICE_ADDRESS},
          queue_down_end,
          handler_,
          connection->GetEventCallbacks([this](uint16_t handle) { this->connections.invalidate(handle); }));
      connections.execute(address, [=](ConnectionManagementCallbacks* callbacks) {
        if (delayed_role_change_ == nullptr) {
          callbacks->OnRoleChange(hci::ErrorCode::SUCCESS, current_role);
        } else if (delayed_role_change_->GetBdAddr() == address) {
          LOG_INFO("Sending delayed role change for %s", delayed_role_change_->GetBdAddr().ToString().c_str());
          callbacks->OnRoleChange(delayed_role_change_->GetStatus(), delayed_role_change_->GetNewRole());
          delayed_role_change_.reset();
        }
      });
      client_handler_->Post(common::BindOnce(
          &ConnectionCallbacks::OnConnectSuccess, common::Unretained(client_callbacks_), std::move(connection)));
    }
    dequeue_next_connection();
  }

  void dequeue_next_connection() {
    if (incoming_connecting_address_set_.empty() && outgoing_connecting_address_.IsEmpty()) {
      while (!pending_outgoing_connections_.empty()) {
@@ -386,6 +325,79 @@ struct classic_impl : public security::ISecurityManagerListener {
    }
  }

  enum class Initiator {
    LOCALLY_INITIATED,
    REMOTE_INITIATED,
  };

  void create_and_announce_connection(
      ConnectionCompleteView connection_complete, Role current_role, Initiator initiator) {
    auto status = connection_complete.GetStatus();
    auto address = connection_complete.GetBdAddr();
    if (client_callbacks_ == nullptr) {
      LOG_WARN("No client callbacks registered for connection");
      return;
    }
    if (status != ErrorCode::SUCCESS) {
      client_handler_->Post(common::BindOnce(
          &ConnectionCallbacks::OnConnectFail, common::Unretained(client_callbacks_), address, status));
      return;
    }
    uint16_t handle = connection_complete.GetConnectionHandle();
    auto queue = std::make_shared<AclConnection::Queue>(10);
    auto queue_down_end = queue->GetDownEnd();
    round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue);
    std::unique_ptr<ClassicAclConnection> connection(
        new ClassicAclConnection(std::move(queue), acl_connection_interface_, handle, address));
    connection->locally_initiated_ = initiator == Initiator::LOCALLY_INITIATED;
    connections.add(
        handle,
        AddressWithType{address, AddressType::PUBLIC_DEVICE_ADDRESS},
        queue_down_end,
        handler_,
        connection->GetEventCallbacks([this](uint16_t handle) { this->connections.invalidate(handle); }));
    connections.execute(address, [=](ConnectionManagementCallbacks* callbacks) {
      if (delayed_role_change_ == nullptr) {
        callbacks->OnRoleChange(hci::ErrorCode::SUCCESS, current_role);
      } else if (delayed_role_change_->GetBdAddr() == address) {
        LOG_INFO("Sending delayed role change for %s", delayed_role_change_->GetBdAddr().ToString().c_str());
        callbacks->OnRoleChange(delayed_role_change_->GetStatus(), delayed_role_change_->GetNewRole());
        delayed_role_change_.reset();
      }
    });
    client_handler_->Post(common::BindOnce(
        &ConnectionCallbacks::OnConnectSuccess, common::Unretained(client_callbacks_), std::move(connection)));
  }

  void on_connection_complete(EventView packet) {
    ConnectionCompleteView connection_complete = ConnectionCompleteView::Create(packet);
    ASSERT(connection_complete.IsValid());
    auto status = connection_complete.GetStatus();
    auto address = connection_complete.GetBdAddr();
    Role current_role = Role::CENTRAL;
    auto initiator = Initiator::LOCALLY_INITIATED;
    if (outgoing_connecting_address_ == address) {
      outgoing_connecting_address_ = Address::kEmpty;
    } else {
      auto incoming_address = incoming_connecting_address_set_.find(address);
      if (incoming_address == incoming_connecting_address_set_.end()) {
        ASSERT_LOG(
            status != ErrorCode::UNKNOWN_CONNECTION,
            "No prior connection request for %s expecting:%s",
            address.ToString().c_str(),
            set_of_incoming_connecting_addresses().c_str());
        LOG_WARN("No matching connection to %s (%s)", address.ToString().c_str(), ErrorCodeText(status).c_str());
        LOG_WARN("Firmware error after RemoteNameRequestCancel?");
        return;
      }
      incoming_connecting_address_set_.erase(incoming_address);
      current_role = Role::PERIPHERAL;
      initiator = Initiator::REMOTE_INITIATED;
    }
    create_and_announce_connection(connection_complete, current_role, initiator);
    dequeue_next_connection();
  }

  void cancel_connect(Address address) {
    if (outgoing_connecting_address_ == address) {
      LOG_INFO("Cannot cancel non-existent connection to %s", address.ToString().c_str());