1 /*
2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #pragma once
18
19 #include <bluetooth/log.h>
20
21 #include <cstddef>
22 #include <cstdint>
23 #include <memory>
24 #include <vector>
25
26 #include "hci/acl_manager/acl_connection.h"
27 #include "hci/address_with_type.h"
28 #include "os/handler.h"
29 #include "os/log.h"
30 #include "packet/packet_view.h"
31
32 namespace bluetooth {
33 namespace hci {
34 namespace acl_manager {
35
36 constexpr size_t kMaxQueuedPacketsPerConnection = 10;
37 constexpr size_t kL2capBasicFrameHeaderSize = 4;
38
39 namespace {
40 // This is a helper class to keep the state of the assembler and expose PacketView<>::Append.
41 class PacketViewForRecombination : public packet::PacketView<packet::kLittleEndian> {
42 public:
PacketViewForRecombination(const PacketView & packetView)43 PacketViewForRecombination(const PacketView& packetView)
44 : PacketView(packetView), received_first_(true) {}
45
PacketViewForRecombination()46 PacketViewForRecombination()
47 : PacketView(PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>())) {}
48
AppendPacketView(packet::PacketView<packet::kLittleEndian> to_append)49 void AppendPacketView(packet::PacketView<packet::kLittleEndian> to_append) {
50 Append(to_append);
51 }
52
ReceivedFirstPacket()53 bool ReceivedFirstPacket() {
54 return received_first_;
55 }
56
57 private:
58 bool received_first_{};
59 };
60
61 // Per spec 5.1 Vol 2 Part B 5.3, ACL link shall carry L2CAP data. Therefore, an ACL packet shall
62 // contain L2CAP PDU. This function returns the PDU size of the L2CAP starting packet, or
63 // kL2capBasicFrameHeaderSize if it's invalid.
GetL2capPduSize(packet::PacketView<packet::kLittleEndian> pdu)64 size_t GetL2capPduSize(packet::PacketView<packet::kLittleEndian> pdu) {
65 if (pdu.size() < 2) {
66 return kL2capBasicFrameHeaderSize; // We need at least 4 bytes to send it to L2CAP
67 }
68 return (static_cast<size_t>(pdu[1]) << 8u) + pdu[0];
69 }
70
71 } // namespace
72
73 struct assembler {
assemblerassembler74 assembler(AddressWithType address_with_type, AclConnection::QueueDownEnd* down_end, os::Handler* handler)
75 : address_with_type_(address_with_type), down_end_(down_end), handler_(handler) {}
76 AddressWithType address_with_type_;
77 AclConnection::QueueDownEnd* down_end_;
78 os::Handler* handler_;
79 PacketViewForRecombination recombination_stage_{};
80 std::shared_ptr<std::atomic_bool> enqueue_registered_ = std::make_shared<std::atomic_bool>(false);
81 std::queue<packet::PacketView<packet::kLittleEndian>> incoming_queue_;
82
~assemblerassembler83 ~assembler() {
84 if (enqueue_registered_->exchange(false)) {
85 down_end_->UnregisterEnqueue();
86 }
87 }
88
89 // Invoked from some external Queue Reactable context
on_data_readyassembler90 std::unique_ptr<packet::PacketView<packet::kLittleEndian>> on_data_ready() {
91 auto packet = incoming_queue_.front();
92 incoming_queue_.pop();
93 if (incoming_queue_.empty() && enqueue_registered_->exchange(false)) {
94 down_end_->UnregisterEnqueue();
95 }
96 return std::make_unique<PacketView<packet::kLittleEndian>>(packet);
97 }
98
on_incoming_packetassembler99 void on_incoming_packet(AclView packet) {
100 PacketView<packet::kLittleEndian> payload = packet.GetPayload();
101 auto broadcast_flag = packet.GetBroadcastFlag();
102 if (broadcast_flag == BroadcastFlag::ACTIVE_PERIPHERAL_BROADCAST) {
103 log::warn("Dropping broadcast from remote");
104 return;
105 }
106 auto packet_boundary_flag = packet.GetPacketBoundaryFlag();
107 if (packet_boundary_flag == PacketBoundaryFlag::FIRST_NON_AUTOMATICALLY_FLUSHABLE) {
108 log::error(
109 "Controller is not allowed to send FIRST_NON_AUTOMATICALLY_FLUSHABLE to host except "
110 "loopback mode");
111 return;
112 }
113 if (packet_boundary_flag == PacketBoundaryFlag::CONTINUING_FRAGMENT) {
114 if (!recombination_stage_.ReceivedFirstPacket()) {
115 log::error("Continuing fragment received without previous first, dropping it.");
116 return;
117 }
118 recombination_stage_.AppendPacketView(payload);
119 } else if (packet_boundary_flag == PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE) {
120 if (recombination_stage_.ReceivedFirstPacket()) {
121 log::error(
122 "Controller sent a starting packet without finishing previous packet. Drop previous "
123 "one.");
124 }
125 recombination_stage_ = payload;
126 }
127 // Check the size of the packet
128 size_t expected_size = GetL2capPduSize(recombination_stage_) + kL2capBasicFrameHeaderSize;
129 if (expected_size < recombination_stage_.size()) {
130 log::info("Packet size doesn't match L2CAP header, dropping it.");
131 recombination_stage_ = PacketViewForRecombination();
132 return;
133 } else if (expected_size > recombination_stage_.size()) {
134 // Wait for the next fragment before sending
135 return;
136 }
137 if (incoming_queue_.size() > kMaxQueuedPacketsPerConnection) {
138 log::error("Dropping packet from {} due to congestion", address_with_type_);
139 recombination_stage_ = PacketViewForRecombination();
140 return;
141 }
142
143 incoming_queue_.push(recombination_stage_);
144 recombination_stage_ = PacketViewForRecombination();
145 if (!enqueue_registered_->exchange(true)) {
146 down_end_->RegisterEnqueue(
147 handler_, common::Bind(&assembler::on_data_ready, common::Unretained(this)));
148 }
149 }
150 };
151
152 } // namespace acl_manager
153 } // namespace hci
154 } // namespace bluetooth
155