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

Commit 8e8badfb authored by Hansong Zhang's avatar Hansong Zhang
Browse files

L2CAP: Add Reassembler to separate outgoing and incoming queue

Previously Scheduler handles both outgoing and incoming packet path, but
they are actually not related to each other. Separating them to
decouple. Now Scheduler only serves outgoing packets, and the new
Reassembler serves incoming packets.

Currently only basic mode is supported. When we add enhanced retransmission
mode, we add logic to it separately.

Bug: 144375926
Test: run_cert.sh and bluetooth_test_gd
Change-Id: I411c0696fc2e6c834ab11e2485ee26d36104095d
parent 1543a7d5
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ filegroup {
        "classic/internal/signalling_manager.cc",
        "classic/l2cap_classic_module.cc",
        "internal/scheduler_fifo.cc",
        "internal/reassembler.cc",
        "le/internal/fixed_channel_impl.cc",
        "le/internal/fixed_channel_service_manager_impl.cc",
        "le/internal/link_manager.cc",
@@ -39,6 +40,7 @@ filegroup {
        "classic/internal/link_manager_test.cc",
        "classic/internal/signalling_manager_test.cc",
        "internal/fixed_channel_allocator_test.cc",
        "internal/reassembler_test.cc",
        "internal/scheduler_fifo_test.cc",
        "l2cap_packet_test.cc",
        "le/internal/fixed_channel_impl_test.cc",
+5 −1
Original line number Diff line number Diff line
@@ -34,7 +34,8 @@ Link::Link(os::Handler* l2cap_handler, std::unique_ptr<hci::AclConnection> acl_c
           l2cap::internal::ParameterProvider* parameter_provider,
           DynamicChannelServiceManagerImpl* dynamic_service_manager,
           FixedChannelServiceManagerImpl* fixed_service_manager)
    : l2cap_handler_(l2cap_handler), acl_connection_(std::move(acl_connection)), scheduler_(std::move(scheduler)),
    : l2cap_handler_(l2cap_handler), acl_connection_(std::move(acl_connection)),
      reassembler_(acl_connection_->GetAclQueueEnd(), l2cap_handler_), scheduler_(std::move(scheduler)),
      parameter_provider_(parameter_provider), dynamic_service_manager_(dynamic_service_manager),
      fixed_service_manager_(fixed_service_manager),
      signalling_manager_(l2cap_handler_, this, dynamic_service_manager_, &dynamic_channel_allocator_,
@@ -59,6 +60,7 @@ void Link::Disconnect() {
std::shared_ptr<FixedChannelImpl> Link::AllocateFixedChannel(Cid cid, SecurityPolicy security_policy) {
  auto channel = fixed_channel_allocator_.AllocateChannel(cid, security_policy);
  scheduler_->AttachChannel(cid, channel->GetQueueDownEnd(), cid);
  reassembler_.AttachChannel(cid, channel->GetQueueDownEnd(), {});
  return channel;
}

@@ -87,6 +89,7 @@ std::shared_ptr<DynamicChannelImpl> Link::AllocateDynamicChannel(Psm psm, Cid re
  auto channel = dynamic_channel_allocator_.AllocateChannel(psm, remote_cid, security_policy);
  if (channel != nullptr) {
    scheduler_->AttachChannel(channel->GetCid(), channel->GetQueueDownEnd(), channel->GetRemoteCid());
    reassembler_.AttachChannel(channel->GetCid(), channel->GetQueueDownEnd(), {});
  }
  return channel;
}
@@ -96,6 +99,7 @@ std::shared_ptr<DynamicChannelImpl> Link::AllocateReservedDynamicChannel(Cid res
  auto channel = dynamic_channel_allocator_.AllocateReservedChannel(reserved_cid, psm, remote_cid, security_policy);
  if (channel != nullptr) {
    scheduler_->AttachChannel(channel->GetCid(), channel->GetQueueDownEnd(), channel->GetRemoteCid());
    reassembler_.AttachChannel(channel->GetCid(), channel->GetQueueDownEnd(), {});
  }
  return channel;
}
+2 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include "l2cap/classic/internal/fixed_channel_service_manager_impl.h"
#include "l2cap/internal/fixed_channel_allocator.h"
#include "l2cap/internal/parameter_provider.h"
#include "l2cap/internal/reassembler.h"
#include "l2cap/internal/scheduler.h"
#include "os/alarm.h"
#include "os/handler.h"
@@ -87,6 +88,7 @@ class Link {
  l2cap::internal::FixedChannelAllocator<FixedChannelImpl, Link> fixed_channel_allocator_{this, l2cap_handler_};
  DynamicChannelAllocator dynamic_channel_allocator_{this, l2cap_handler_};
  std::unique_ptr<hci::AclConnection> acl_connection_;
  l2cap::internal::Reassembler reassembler_;
  std::unique_ptr<l2cap::internal::Scheduler> scheduler_;
  l2cap::internal::ParameterProvider* parameter_provider_;
  DynamicChannelServiceManagerImpl* dynamic_service_manager_;
+92 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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 "l2cap/internal/reassembler.h"
#include "common/bidi_queue.h"
#include "l2cap/cid.h"
#include "l2cap/l2cap_packets.h"
#include "packet/base_packet_builder.h"
#include "packet/packet_view.h"
#

namespace bluetooth {
namespace l2cap {
namespace internal {
Reassembler::Reassembler(LowerQueueUpEnd* link_queue_up_end, os::Handler* handler)
    : link_queue_up_end_(link_queue_up_end), handler_(handler) {
  ASSERT(link_queue_up_end_ != nullptr && handler_ != nullptr);
  link_queue_up_end_->RegisterDequeue(
      handler_, common::Bind(&Reassembler::link_queue_dequeue_callback, common::Unretained(this)));
}

Reassembler::~Reassembler() {
  link_queue_up_end_->UnregisterDequeue();
}

void Reassembler::AttachChannel(Cid cid, Reassembler::UpperQueueDownEnd* channel_down_end,
                                Reassembler::ChannelConfigurationOptions options) {
  ASSERT_LOG(channel_map_.find(cid) == channel_map_.end(), "Channel is already attached");
  auto pair = ChannelBufferAndOptions(channel_down_end, options);
  channel_map_.emplace(std::piecewise_construct, std::forward_as_tuple(cid),
                       std::forward_as_tuple(channel_down_end, options));
}

void Reassembler::DetachChannel(Cid cid) {
  ASSERT_LOG(channel_map_.find(cid) != channel_map_.end(), "Channel is not attached");
  channel_map_.erase(cid);
}

void Reassembler::link_queue_dequeue_callback() {
  auto packet = link_queue_up_end_->TryDequeue();
  auto basic_frame_view = BasicFrameView::Create(*packet);
  if (!basic_frame_view.IsValid()) {
    LOG_WARN("Received an invalid basic frame");
    return;
  }
  Cid cid = static_cast<Cid>(basic_frame_view.GetChannelId());
  auto channel = channel_map_.find(cid);
  if (channel == channel_map_.end()) {
    LOG_WARN("Received a packet with invalid cid: %d", cid);
    return;  // Channel is not attached to scheduler
  }

  auto channel_mode = channel->second.options_.mode_;
  switch (channel_mode) {
    case RetransmissionAndFlowControlModeOption::L2CAP_BASIC:
      handle_basic_mode_packet(cid, basic_frame_view);
      break;
    case RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION:
      handle_enhanced_retransmission_mode_packet(cid, std::move(basic_frame_view));
      break;
    default:
      LOG_WARN("channel mode is not supported: %d", static_cast<int>(channel_mode));
  }
}

void Reassembler::handle_basic_mode_packet(Cid cid, const BasicFrameView& view) {
  auto channel = channel_map_.find(cid);
  auto& enqueue_buffer = channel->second.enqueue_buffer_;

  enqueue_buffer.Enqueue(std::make_unique<PacketView<kLittleEndian>>(view.GetPayload()), handler_);
}

void Reassembler::handle_enhanced_retransmission_mode_packet(Cid cid, BasicFrameView view) {
  LOG_ERROR("Enhanced retransmission mode is not implemented");
}

}  // namespace internal
}  // namespace l2cap
}  // namespace bluetooth
+90 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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 <unordered_map>
#include <utility>

#include "common/bidi_queue.h"
#include "l2cap/cid.h"
#include "l2cap/l2cap_packets.h"
#include "l2cap/mtu.h"
#include "os/queue.h"
#include "packet/base_packet_builder.h"
#include "packet/packet_view.h"

namespace bluetooth {
namespace l2cap {
namespace internal {

/**
 * Handle the reassembly of L2CAP SDU from PDU.
 * Dequeue incoming packets from LinkQueueUpEnd, and enqueue it to ChannelQueueDownEnd. Note: If a channel
 * cannot dequeue from ChannelQueueDownEnd so that the buffer for incoming packet is full, further incoming packets will
 * be dropped.
 * The Reassembler keeps the reference to ChannelImpl objects, because it needs to check channel mode and parameters.
 */
class Reassembler {
 public:
  using UpperEnqueue = packet::PacketView<packet::kLittleEndian>;
  using UpperDequeue = packet::BasePacketBuilder;
  using UpperQueueDownEnd = common::BidiQueueEnd<UpperEnqueue, UpperDequeue>;
  using LowerEnqueue = UpperDequeue;
  using LowerDequeue = UpperEnqueue;
  using LowerQueueUpEnd = common::BidiQueueEnd<LowerEnqueue, LowerDequeue>;

  Reassembler(LowerQueueUpEnd* link_queue_up_end, os::Handler* handler);
  ~Reassembler();

  struct ChannelConfigurationOptions {
    Mtu incoming_mtu_ = kDefaultClassicMtu;
    RetransmissionAndFlowControlModeOption mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
    // TODO: Add all RetransmissionAndFlowControlConfigurationOptions
    FcsType fcs_type_ = FcsType::NO_FCS;
  };

  /**
   * Attach a channel for packet reassembly.
   * If the channel is reconfigured, signalling manager should detach channel and attach channel again.
   */
  void AttachChannel(Cid cid, UpperQueueDownEnd* channel_down_end, ChannelConfigurationOptions options);

  /**
   * Detach a channel for packet reassembly. Incoming packets won't be delivered to the specified cid.
   */
  void DetachChannel(Cid cid);

 private:
  struct ChannelBufferAndOptions {
    ChannelBufferAndOptions(UpperQueueDownEnd* queue_end, ChannelConfigurationOptions options)
        : enqueue_buffer_(queue_end), options_(std::move(options)) {}
    os::EnqueueBuffer<UpperEnqueue> enqueue_buffer_;
    ChannelConfigurationOptions options_;
  };

  LowerQueueUpEnd* link_queue_up_end_;
  os::Handler* handler_;
  std::unordered_map<Cid, ChannelBufferAndOptions> channel_map_;

  void link_queue_dequeue_callback();
  void handle_basic_mode_packet(Cid cid, const BasicFrameView& view);
  void handle_enhanced_retransmission_mode_packet(Cid cid, BasicFrameView view);
};

}  // namespace internal
}  // namespace l2cap
}  // namespace bluetooth
Loading