/* * Copyright 2020 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 #include #include #include #include #include "hci/acl_manager/acl_connection.h" #include "hci/address_with_type.h" #include "os/handler.h" #include "os/log.h" #include "packet/packet_view.h" namespace bluetooth { namespace hci { namespace acl_manager { constexpr size_t kMaxQueuedPacketsPerConnection = 10; constexpr size_t kL2capBasicFrameHeaderSize = 4; namespace { // This is a helper class to keep the state of the assembler and expose PacketView<>::Append. class PacketViewForRecombination : public packet::PacketView { public: PacketViewForRecombination(const PacketView& packetView) : PacketView(packetView), received_first_(true) {} PacketViewForRecombination() : PacketView(PacketView(std::make_shared>())) {} void AppendPacketView(packet::PacketView to_append) { Append(to_append); } bool ReceivedFirstPacket() { return received_first_; } private: bool received_first_{}; }; // Per spec 5.1 Vol 2 Part B 5.3, ACL link shall carry L2CAP data. Therefore, an ACL packet shall // contain L2CAP PDU. This function returns the PDU size of the L2CAP starting packet, or // kL2capBasicFrameHeaderSize if it's invalid. size_t GetL2capPduSize(packet::PacketView pdu) { if (pdu.size() < 2) { return kL2capBasicFrameHeaderSize; // We need at least 4 bytes to send it to L2CAP } return (static_cast(pdu[1]) << 8u) + pdu[0]; } } // namespace struct assembler { assembler(AddressWithType address_with_type, AclConnection::QueueDownEnd* down_end, os::Handler* handler) : address_with_type_(address_with_type), down_end_(down_end), handler_(handler) {} AddressWithType address_with_type_; AclConnection::QueueDownEnd* down_end_; os::Handler* handler_; PacketViewForRecombination recombination_stage_{}; std::shared_ptr enqueue_registered_ = std::make_shared(false); std::queue> incoming_queue_; ~assembler() { if (enqueue_registered_->exchange(false)) { down_end_->UnregisterEnqueue(); } } // Invoked from some external Queue Reactable context std::unique_ptr> on_data_ready() { auto packet = incoming_queue_.front(); incoming_queue_.pop(); if (incoming_queue_.empty() && enqueue_registered_->exchange(false)) { down_end_->UnregisterEnqueue(); } return std::make_unique>(packet); } void on_incoming_packet(AclView packet) { PacketView payload = packet.GetPayload(); auto broadcast_flag = packet.GetBroadcastFlag(); if (broadcast_flag == BroadcastFlag::ACTIVE_PERIPHERAL_BROADCAST) { log::warn("Dropping broadcast from remote"); return; } auto packet_boundary_flag = packet.GetPacketBoundaryFlag(); if (packet_boundary_flag == PacketBoundaryFlag::FIRST_NON_AUTOMATICALLY_FLUSHABLE) { log::error( "Controller is not allowed to send FIRST_NON_AUTOMATICALLY_FLUSHABLE to host except " "loopback mode"); return; } if (packet_boundary_flag == PacketBoundaryFlag::CONTINUING_FRAGMENT) { if (!recombination_stage_.ReceivedFirstPacket()) { log::error("Continuing fragment received without previous first, dropping it."); return; } recombination_stage_.AppendPacketView(payload); } else if (packet_boundary_flag == PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE) { if (recombination_stage_.ReceivedFirstPacket()) { log::error( "Controller sent a starting packet without finishing previous packet. Drop previous " "one."); } recombination_stage_ = payload; } // Check the size of the packet size_t expected_size = GetL2capPduSize(recombination_stage_) + kL2capBasicFrameHeaderSize; if (expected_size < recombination_stage_.size()) { log::info("Packet size doesn't match L2CAP header, dropping it."); recombination_stage_ = PacketViewForRecombination(); return; } else if (expected_size > recombination_stage_.size()) { // Wait for the next fragment before sending return; } if (incoming_queue_.size() > kMaxQueuedPacketsPerConnection) { log::error("Dropping packet from {} due to congestion", address_with_type_); recombination_stage_ = PacketViewForRecombination(); return; } incoming_queue_.push(recombination_stage_); recombination_stage_ = PacketViewForRecombination(); if (!enqueue_registered_->exchange(true)) { down_end_->RegisterEnqueue( handler_, common::Bind(&assembler::on_data_ready, common::Unretained(this))); } } }; } // namespace acl_manager } // namespace hci } // namespace bluetooth