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

Commit 9068f477 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge changes I6c705777,If653cb88,I8284604f,I7849dde9

* changes:
  Only fragment and reassemble ISO
  Skip fragmentation for SCO
  Sco: Avoid BTE
  Remove IncomingAcl
parents 52f024b8 2d22ba39
Loading
Loading
Loading
Loading
+0 −27
Original line number Diff line number Diff line
@@ -73,30 +73,3 @@ cc_test {
        "libchrome",
    ],
}

cc_test {
    name: "net_test_hci_fragmenter_native",
    static_libs: [
        "libbase",
        "libchrome",
    ],
    test_suites: ["device-tests"],
    defaults: [
        "bluetooth_gtest_x86_asan_workaround",
        "fluoride_test_defaults",
        "mts_defaults",
    ],
    local_include_dirs: [
        "include",
    ],
    include_dirs: [
        "packages/modules/Bluetooth/system",
        "packages/modules/Bluetooth/system/gd",
        "packages/modules/Bluetooth/system/osi/test",
        "packages/modules/Bluetooth/system/stack/include",
    ],
    srcs: [
        "src/buffer_allocator.cc",
        "test/packet_fragmenter_host_test.cc",
    ],
}
+0 −1
Original line number Diff line number Diff line
@@ -33,7 +33,6 @@
#define MSG_SUB_EVT_MASK 0x00FF /* eq. BT_SUB_EVT_MASK */

/* Message event ID passed from Host/Controller lib to stack */
#define MSG_HC_TO_STACK_HCI_ACL 0x1100      /* eq. BT_EVT_TO_BTU_HCI_ACL */
#define MSG_HC_TO_STACK_HCI_SCO 0x1200      /* eq. BT_EVT_TO_BTU_HCI_SCO */
#define MSG_HC_TO_STACK_HCI_ERR 0x1300      /* eq. BT_EVT_TO_BTU_HCIT_ERR */
#define MSG_HC_TO_STACK_HCI_ISO 0x1700      /* eq. BT_EVT_TO_BTU_HCI_ISO */
+2 −9
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@
#include "osi/include/allocator.h"
#include "stack/include/bt_hdr.h"

typedef void (*transmit_finished_cb)(BT_HDR* packet, bool all_fragments_sent);
typedef void (*packet_reassembled_cb)(BT_HDR* packet);
typedef void (*packet_fragmented_cb)(BT_HDR* packet,
                                     bool send_transmit_finished);
@@ -34,10 +33,6 @@ typedef struct {

  // Called for every completely reassembled packet.
  packet_reassembled_cb reassembled;

  // Called when the fragmenter finishes sending all requested fragments,
  // but the packet has not been entirely sent.
  transmit_finished_cb transmit_finished;
} packet_fragmenter_callbacks_t;

typedef struct packet_fragmenter_t {
@@ -51,10 +46,8 @@ typedef struct packet_fragmenter_t {
  // callback.
  void (*fragment_and_dispatch)(BT_HDR* packet);
  // If |packet| is a complete packet, forwards to the reassembled callback.
  // Otherwise
  // holds onto it until all fragments arrive, at which point the reassembled
  // callback is called
  // with the reassembled data.
  // Otherwise holds onto it until all fragments arrive, at which point the
  // reassembled callback is called with the reassembled data.
  void (*reassemble_and_dispatch)(BT_HDR* packet);
} packet_fragmenter_t;

+6 −158
Original line number Diff line number Diff line
@@ -30,12 +30,8 @@
#include "device/include/controller.h"
#include "hci/include/buffer_allocator.h"
#include "osi/include/log.h"
#include "osi/include/osi.h"
#include "stack/include/bt_hdr.h"

// 2 bytes for handle, 2 bytes for data length (Volume 2, Part E, 5.4.2)
#define HCI_ACL_PREAMBLE_SIZE 4

#define HCI_ISO_BF_FIRST_FRAGMENTED_PACKET (0)
#define HCI_ISO_BF_CONTINUATION_FRAGMENT_PACKET (1)
#define HCI_ISO_BF_COMPLETE_PACKET (2)
@@ -84,39 +80,21 @@ static const allocator_t* buffer_allocator;
static const controller_t* controller;
static const packet_fragmenter_callbacks_t* callbacks;

static std::unordered_map<uint16_t /* handle */, BT_HDR*> partial_packets;
static std::unordered_map<uint16_t /* handle */, BT_HDR*> partial_iso_packets;

static void init(const packet_fragmenter_callbacks_t* result_callbacks) {
  callbacks = result_callbacks;
}

static void cleanup() {
  partial_packets.clear();
  partial_iso_packets.clear();
}

static bool check_uint16_overflow(uint16_t a, uint16_t b) {
  return (UINT16_MAX - a) < b;
}

static void fragment_and_dispatch_iso(BT_HDR* packet);
static void cleanup() { partial_iso_packets.clear(); }

static void fragment_and_dispatch(BT_HDR* packet) {
  CHECK(packet != NULL);

  uint16_t event = packet->event & MSG_EVT_MASK;

  if (event == MSG_HC_TO_STACK_HCI_SCO) {
    callbacks->fragmented(packet, true);
  } else if (event == MSG_STACK_TO_HC_HCI_ISO) {
    fragment_and_dispatch_iso(packet);
  } else {
    callbacks->fragmented(packet, true);
  }
}
  CHECK(event == MSG_STACK_TO_HC_HCI_ISO);

static void fragment_and_dispatch_iso(BT_HDR* packet) {
  uint8_t* stream = packet->data + packet->offset;
  uint16_t max_data_size = controller->get_iso_data_size();
  uint16_t max_packet_size = max_data_size + HCI_ISO_PREAMBLE_SIZE;
@@ -162,7 +140,7 @@ static void fragment_and_dispatch_iso(BT_HDR* packet) {
  callbacks->fragmented(packet, true);
}

static void reassemble_and_dispatch_iso(UNUSED_ATTR BT_HDR* packet) {
static void reassemble_and_dispatch(UNUSED_ATTR BT_HDR* packet) {
  uint8_t* stream = packet->data;
  uint16_t handle;
  uint16_t iso_length;
@@ -170,6 +148,9 @@ static void reassemble_and_dispatch_iso(UNUSED_ATTR BT_HDR* packet) {
  BT_HDR* partial_packet;
  uint16_t iso_full_len;

  uint16_t event = packet->event & MSG_EVT_MASK;
  CHECK(event == MSG_HC_TO_STACK_HCI_ISO);

  STREAM_TO_UINT16(handle, stream);
  STREAM_TO_UINT16(iso_length, stream);
  // last 2 bits is RFU
@@ -343,139 +324,6 @@ static void reassemble_and_dispatch_iso(UNUSED_ATTR BT_HDR* packet) {
  }
}

static void reassemble_and_dispatch(BT_HDR* packet) {
  if ((packet->event & MSG_EVT_MASK) == MSG_HC_TO_STACK_HCI_ACL) {
    uint8_t* stream = packet->data;
    uint16_t handle;
    uint16_t acl_length;

    STREAM_TO_UINT16(handle, stream);
    STREAM_TO_UINT16(acl_length, stream);

    CHECK(acl_length == packet->len - HCI_ACL_PREAMBLE_SIZE);

    uint8_t boundary_flag = GET_BOUNDARY_FLAG(handle);
    uint8_t broadcast_flag = GET_BROADCAST_FLAG(handle);
    handle = handle & HANDLE_MASK;

    if (broadcast_flag != POINT_TO_POINT) {
      LOG_WARN("dropping broadcast packet");
      buffer_allocator->free(packet);
      return;
    }

    if (boundary_flag == START_PACKET_BOUNDARY) {
      if (acl_length < 2) {
        LOG_WARN("%s invalid acl_length %d", __func__, acl_length);
        buffer_allocator->free(packet);
        return;
      }
      uint16_t l2cap_length;
      STREAM_TO_UINT16(l2cap_length, stream);
      auto map_iter = partial_packets.find(handle);
      if (map_iter != partial_packets.end()) {
        LOG_WARN(
            "%s found unfinished packet for handle with start packet. "
            "Dropping old.",
            __func__);

        BT_HDR* hdl = map_iter->second;
        partial_packets.erase(map_iter);
        buffer_allocator->free(hdl);
      }

      if (acl_length < L2CAP_HEADER_PDU_LEN_SIZE) {
        LOG_WARN("%s L2CAP packet too small (%d < %d). Dropping it.", __func__,
                 packet->len, L2CAP_HEADER_PDU_LEN_SIZE);
        buffer_allocator->free(packet);
        return;
      }

      uint16_t full_length =
          l2cap_length + L2CAP_HEADER_SIZE + HCI_ACL_PREAMBLE_SIZE;

      // Check for buffer overflow and that the full packet size + BT_HDR size
      // is less than the max buffer size
      if (check_uint16_overflow(l2cap_length,
                                (L2CAP_HEADER_SIZE + HCI_ACL_PREAMBLE_SIZE)) ||
          ((full_length + sizeof(BT_HDR)) > BT_DEFAULT_BUFFER_SIZE)) {
        LOG_ERROR("%s Dropping L2CAP packet with invalid length (%d).",
                  __func__, l2cap_length);
        buffer_allocator->free(packet);
        return;
      }

      if (full_length <= packet->len) {
        if (full_length < packet->len)
          LOG_WARN("%s found l2cap full length %d less than the hci length %d.",
                   __func__, l2cap_length, packet->len);

        callbacks->reassembled(packet);
        return;
      }

      BT_HDR* partial_packet =
          (BT_HDR*)buffer_allocator->alloc(full_length + sizeof(BT_HDR));
      partial_packet->event = packet->event;
      partial_packet->len = full_length;
      partial_packet->offset = packet->len;

      memcpy(partial_packet->data, packet->data, packet->len);

      // Update the ACL data size to indicate the full expected length
      stream = partial_packet->data;
      STREAM_SKIP_UINT16(stream);  // skip the handle
      UINT16_TO_STREAM(stream, full_length - HCI_ACL_PREAMBLE_SIZE);

      partial_packets[handle] = partial_packet;

      // Free the old packet buffer, since we don't need it anymore
      buffer_allocator->free(packet);
    } else {
      auto map_iter = partial_packets.find(handle);
      if (map_iter == partial_packets.end()) {
        LOG_WARN("%s got continuation for unknown packet. Dropping it.",
                 __func__);
        buffer_allocator->free(packet);
        return;
      }
      BT_HDR* partial_packet = map_iter->second;

      packet->offset = HCI_ACL_PREAMBLE_SIZE;
      uint16_t projected_offset =
          partial_packet->offset + (packet->len - HCI_ACL_PREAMBLE_SIZE);
      if ((packet->len - packet->offset) >
          (partial_packet->len - partial_packet->offset)) {
        LOG_WARN(
            "%s got packet which would exceed expected length of %d. "
            "Truncating.",
            __func__, partial_packet->len);
        packet->len = (partial_packet->len - partial_packet->offset) + packet->offset;
        projected_offset = partial_packet->len;
      }

      memcpy(partial_packet->data + partial_packet->offset,
             packet->data + packet->offset, packet->len - packet->offset);

      // Free the old packet buffer, since we don't need it anymore
      buffer_allocator->free(packet);
      partial_packet->offset = projected_offset;

      if (partial_packet->offset == partial_packet->len) {
        partial_packets.erase(handle);
        partial_packet->offset = 0;
        callbacks->reassembled(partial_packet);
      }
    }
  } else if ((packet->event & MSG_EVT_MASK) == MSG_HC_TO_STACK_HCI_SCO) {
    callbacks->reassembled(packet);
  } else if ((packet->event & MSG_EVT_MASK) == MSG_HC_TO_STACK_HCI_ISO) {
    reassemble_and_dispatch_iso(packet);
  } else {
    callbacks->reassembled(packet);
  }
}

static const packet_fragmenter_t interface = {init, cleanup,

                                              fragment_and_dispatch,
+0 −424
Original line number Diff line number Diff line
/*
 * 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.
 */

#include <base/logging.h>
#include <gtest/gtest.h>

#include <cstdint>
#include <queue>

#include "hci/src/packet_fragmenter.cc"
#include "osi/include/allocator.h"
#include "osi/test/AllocationTestHarness.h"
#include "stack/include/bt_hdr.h"

void allocation_tracker_uninit(void);

enum kPacketOrder {
  kStart = 1,
  kContinuation = 2,
};

struct AclPacketHeader {
  struct {
    uint16_t handle : 12;
    uint16_t continuation : 1;
    uint16_t start : 1;
    uint16_t reserved : 2;
  } s;
  uint16_t length;

  uint16_t GetRawHandle() const { return *(uint16_t*)(this); }

  uint16_t GetHandle() const { return s.handle; }
  uint16_t GetLength() const { return length; }
} __attribute__((packed));

struct L2capPacketHeader {
  uint16_t length;
  uint16_t cid;
} __attribute__((packed));

struct AclL2capPacketHeader {
  struct AclPacketHeader acl_header;
  struct L2capPacketHeader l2cap_header;
} __attribute__((packed));

namespace {

constexpr uint16_t kHandle = 0x123;
constexpr uint16_t kCid = 0x4567;
constexpr uint16_t kMaxPacketSize = BT_DEFAULT_BUFFER_SIZE - sizeof(BT_HDR) -
                                    L2CAP_HEADER_SIZE - HCI_ACL_PREAMBLE_SIZE;
constexpr size_t kTypicalPacketSizes[] = {
    1, 2, 3, 4, 8, 16, 32, 64, 127, 128, 129, 256, 1024, 2048, kMaxPacketSize};
constexpr size_t kNumberOfTypicalPacketSizes =
    sizeof(kTypicalPacketSizes) / sizeof(kTypicalPacketSizes[0]);

void FreeBuffer(BT_HDR* bt_hdr) { osi_free(bt_hdr); }

struct TestMutables {
  struct {
    int access_count_{0};
  } fragmented;
  struct {
    int access_count_{0};
    std::queue<std::unique_ptr<BT_HDR, decltype(&FreeBuffer)>> queue;
  } reassembled;
  struct {
    int access_count_{0};
  } transmit_finished;
};

TestMutables test_state_;

void OnFragmented(BT_HDR* packet, bool send_transmit_finished) {
  test_state_.fragmented.access_count_++;
}

void OnReassembled(BT_HDR* packet) {
  test_state_.reassembled.access_count_++;
  test_state_.reassembled.queue.push(
      std::unique_ptr<BT_HDR, decltype(&FreeBuffer)>(packet, &FreeBuffer));
}

void OnTransmitFinished(BT_HDR* packet, bool all_fragments_sent) {
  test_state_.transmit_finished.access_count_++;
}

packet_fragmenter_callbacks_t result_callbacks = {
    .fragmented = OnFragmented,
    .reassembled = OnReassembled,
    .transmit_finished = OnTransmitFinished,
};

AclPacketHeader* AclHeader(BT_HDR* packet) {
  return (AclPacketHeader*)packet->data;
}
L2capPacketHeader* L2capHeader(BT_HDR* packet) {
  return &((AclL2capPacketHeader*)packet->data)->l2cap_header;
}

uint8_t* Data(BT_HDR* packet) {
  AclPacketHeader* acl_header =
      reinterpret_cast<AclPacketHeader*>(packet->data);
  return acl_header->s.start
             ? (uint8_t*)(packet->data + sizeof(AclL2capPacketHeader))
             : (uint8_t*)(packet->data + sizeof(AclPacketHeader));
}

}  // namespace

// Needed for linkage
const controller_t* controller_get_interface() { return nullptr; }

/**
 * Test class to test selected functionality in hci/src/hci_layer.cc
 */
class HciPacketFragmenterTest : public AllocationTestHarness {
 protected:
  void SetUp() override {
    AllocationTestHarness::SetUp();
    // Disable our allocation tracker to allow ASAN full range
    allocation_tracker_uninit();
    packet_fragmenter_ = packet_fragmenter_get_interface();
    packet_fragmenter_->init(&result_callbacks);
    test_state_ = TestMutables();
  }

  void TearDown() override {
    FlushPartialPackets();
    while (!test_state_.reassembled.queue.empty()) {
      test_state_.reassembled.queue.pop();
    }
    packet_fragmenter_->cleanup();
    AllocationTestHarness::TearDown();
  }
  const packet_fragmenter_t* packet_fragmenter_;

  // Start acl packet
  BT_HDR* AllocateL2capPacket(size_t l2cap_length,
                              const std::vector<uint8_t> data) const {
    auto packet =
        AllocateAclPacket(data.size() + sizeof(L2capPacketHeader), kStart);
    L2capHeader(packet)->length = l2cap_length;
    L2capHeader(packet)->cid = kCid;
    std::copy(data.cbegin(), data.cend(), Data(packet));
    return packet;
  }

  // Continuation acl packet
  BT_HDR* AllocateL2capPacket(const std::vector<uint8_t> data) const {
    auto packet = AllocateAclPacket(data.size(), kContinuation);
    std::copy(data.cbegin(), data.cend(), Data(packet));
    return packet;
  }

  const std::vector<uint8_t> CreateData(size_t size) const {
    CHECK(size > 0);
    std::vector<uint8_t> v(size);
    uint8_t sum = 0;
    for (size_t s = 0; s < size; s++) {
      sum += v[s] = s;
    }
    v[0] = (~sum + 1);  // First byte has sum complement
    return v;
  }

  // Verify packet integrity
  bool VerifyData(const uint8_t* data, size_t size) const {
    CHECK(size > 0);
    uint8_t sum = 0;
    for (size_t s = 0; s < size; s++) {
      sum += data[s];
    }
    return sum == 0;
  }

 private:
  BT_HDR* AllocateAclPacket(size_t acl_length,
                            kPacketOrder packet_order) const {
    BT_HDR* packet = AllocatePacket(sizeof(AclPacketHeader) + acl_length,
                                    MSG_HC_TO_STACK_HCI_ACL);
    AclHeader(packet)->s.handle = kHandle;
    AclHeader(packet)->length = acl_length;
    switch (packet_order) {
      case kStart:
        AclHeader(packet)->s.start = 1;
        break;
      case kContinuation:
        AclHeader(packet)->s.continuation = 1;
        break;
    }
    return packet;
  }

  BT_HDR* AllocatePacket(size_t packet_length, uint16_t event_mask) const {
    BT_HDR* packet =
        static_cast<BT_HDR*>(osi_calloc(sizeof(BT_HDR) + packet_length));
    packet->event = event_mask;
    packet->len = static_cast<uint16_t>(packet_length);
    return packet;
  }

  void FlushPartialPackets() const {
    while (!partial_packets.empty()) {
      BT_HDR* partial_packet = partial_packets.at(kHandle);
      partial_packets.erase(kHandle);
      osi_free(partial_packet);
    }
  }
};

TEST_F(HciPacketFragmenterTest, TestStruct_Handle) {
  AclPacketHeader acl_header;
  memset(&acl_header, 0, sizeof(acl_header));

  for (uint16_t h = 0; h < UINT16_MAX; h++) {
    acl_header.s.handle = h;
    CHECK(acl_header.GetHandle() == (h & HANDLE_MASK));
    CHECK(acl_header.s.continuation == 0);
    CHECK(acl_header.s.start == 0);
    CHECK(acl_header.s.reserved == 0);

    CHECK((acl_header.GetRawHandle() & HANDLE_MASK) == (h & HANDLE_MASK));
    GET_BOUNDARY_FLAG(acl_header.GetRawHandle() == 0);
  }
}

TEST_F(HciPacketFragmenterTest, TestStruct_Continuation) {
  AclPacketHeader acl_header;
  memset(&acl_header, 0, sizeof(acl_header));

  for (uint16_t h = 0; h < UINT16_MAX; h++) {
    acl_header.s.continuation = h;
    CHECK(acl_header.GetHandle() == 0);
    CHECK(acl_header.s.continuation == (h & 0x1));
    CHECK(acl_header.s.start == 0);
    CHECK(acl_header.s.reserved == 0);

    CHECK((acl_header.GetRawHandle() & HANDLE_MASK) == 0);
    GET_BOUNDARY_FLAG(acl_header.GetRawHandle() == (h & 0x3));
  }
}

TEST_F(HciPacketFragmenterTest, TestStruct_Start) {
  AclPacketHeader acl_header;
  memset(&acl_header, 0, sizeof(acl_header));

  for (uint16_t h = 0; h < UINT16_MAX; h++) {
    acl_header.s.start = h;
    CHECK(acl_header.GetHandle() == 0);
    CHECK(acl_header.s.continuation == 0);
    CHECK(acl_header.s.start == (h & 0x1));
    CHECK(acl_header.s.reserved == 0);

    CHECK((acl_header.GetRawHandle() & HANDLE_MASK) == 0);
    GET_BOUNDARY_FLAG(acl_header.GetRawHandle() == (h & 0x3));
  }
}

TEST_F(HciPacketFragmenterTest, TestStruct_Reserved) {
  AclPacketHeader acl_header;
  memset(&acl_header, 0, sizeof(acl_header));

  for (uint16_t h = 0; h < UINT16_MAX; h++) {
    acl_header.s.reserved = h;
    CHECK(acl_header.GetHandle() == 0);
    CHECK(acl_header.s.continuation == 0);
    CHECK(acl_header.s.start == 0);
    CHECK(acl_header.s.reserved == (h & 0x3));
  }
}
TEST_F(HciPacketFragmenterTest, CreateAndVerifyPackets) {
  const size_t size_check[] = {1,  2,   3,   4,   8,   16,   32,
                               64, 127, 128, 129, 256, 1024, 0xfff0};
  const std::vector<size_t> sizes(
      size_check, size_check + sizeof(size_check) / sizeof(size_check[0]));

  for (const auto packet_size : sizes) {
    const std::vector<uint8_t> data = CreateData(packet_size);
    uint8_t buf[packet_size];
    std::copy(data.cbegin(), data.cend(), buf);
    CHECK(VerifyData(buf, packet_size));
  }
}

TEST_F(HciPacketFragmenterTest, OnePacket_Immediate) {
  const std::vector<size_t> sizes(
      kTypicalPacketSizes, kTypicalPacketSizes + kNumberOfTypicalPacketSizes);

  int reassembled_access_count = 0;
  for (const auto packet_size : sizes) {
    const std::vector<uint8_t> data = CreateData(packet_size);
    reassemble_and_dispatch(AllocateL2capPacket(data.size(), data));

    CHECK(partial_packets.size() == 0);
    CHECK(test_state_.reassembled.access_count_ == ++reassembled_access_count);
    auto packet = std::move(test_state_.reassembled.queue.front());
    test_state_.reassembled.queue.pop();
    CHECK(VerifyData(Data(packet.get()), packet_size));
  }
}

TEST_F(HciPacketFragmenterTest, OnePacket_ImmediateTooBig) {
  const size_t packet_size = kMaxPacketSize + 1;
  const std::vector<uint8_t> data = CreateData(packet_size);
  reassemble_and_dispatch(AllocateL2capPacket(data.size(), data));

  CHECK(partial_packets.size() == 0);
  CHECK(test_state_.reassembled.access_count_ == 0);
}

TEST_F(HciPacketFragmenterTest, ThreePackets_Immediate) {
  const size_t packet_size = 512;
  const std::vector<uint8_t> data = CreateData(packet_size);
  reassemble_and_dispatch(AllocateL2capPacket(data.size(), data));
  reassemble_and_dispatch(AllocateL2capPacket(data.size(), data));
  reassemble_and_dispatch(AllocateL2capPacket(data.size(), data));
  CHECK(partial_packets.size() == 0);
  CHECK(test_state_.reassembled.access_count_ == 3);
}

TEST_F(HciPacketFragmenterTest, OnePacket_SplitTwo) {
  const std::vector<size_t> sizes(
      kTypicalPacketSizes, kTypicalPacketSizes + kNumberOfTypicalPacketSizes);

  int reassembled_access_count = 0;
  for (auto packet_size : sizes) {
    const std::vector<uint8_t> data = CreateData(packet_size);
    const std::vector<uint8_t> part1(data.cbegin(),
                                     data.cbegin() + packet_size / 2);
    reassemble_and_dispatch(AllocateL2capPacket(data.size(), part1));

    CHECK(partial_packets.size() == 1);
    CHECK(test_state_.reassembled.access_count_ == reassembled_access_count);

    const std::vector<uint8_t> part2(data.cbegin() + packet_size / 2,
                                     data.cend());
    reassemble_and_dispatch(AllocateL2capPacket(part2));

    CHECK(partial_packets.size() == 0);
    CHECK(test_state_.reassembled.access_count_ == ++reassembled_access_count);

    auto packet = std::move(test_state_.reassembled.queue.front());
    test_state_.reassembled.queue.pop();
    CHECK(VerifyData(Data(packet.get()), packet_size));
  }
}

TEST_F(HciPacketFragmenterTest, OnePacket_SplitALot) {
  const size_t packet_size = 512;
  const size_t stride = 2;

  const std::vector<uint8_t> data = CreateData(packet_size);
  const std::vector<uint8_t> first_part(data.cbegin(), data.cbegin() + stride);
  reassemble_and_dispatch(AllocateL2capPacket(data.size(), first_part));
  CHECK(partial_packets.size() == 1);

  for (size_t i = 2; i < packet_size - stride; i += stride) {
    const std::vector<uint8_t> middle_part(data.cbegin() + i,
                                           data.cbegin() + i + stride);
    reassemble_and_dispatch(AllocateL2capPacket(middle_part));
  }
  CHECK(partial_packets.size() == 1);
  CHECK(test_state_.reassembled.access_count_ == 0);

  const std::vector<uint8_t> last_part(data.cbegin() + packet_size - stride,
                                       data.cend());
  reassemble_and_dispatch(AllocateL2capPacket(last_part));

  CHECK(partial_packets.size() == 0);
  CHECK(test_state_.reassembled.access_count_ == 1);
  auto packet = std::move(test_state_.reassembled.queue.front());
  CHECK(VerifyData(Data(packet.get()), packet_size));
}

TEST_F(HciPacketFragmenterTest, TwoPacket_InvalidLength) {
  const size_t packet_size = UINT16_MAX;
  const std::vector<uint8_t> data = CreateData(packet_size);
  const std::vector<uint8_t> first_part(data.cbegin(),
                                        data.cbegin() + packet_size / 2);
  reassemble_and_dispatch(AllocateL2capPacket(data.size(), first_part));

  CHECK(partial_packets.size() == 0);
  CHECK(test_state_.reassembled.access_count_ == 0);

  const std::vector<uint8_t> second_part(data.cbegin() + packet_size / 2,
                                         data.cend());
  reassemble_and_dispatch(AllocateL2capPacket(second_part));

  CHECK(partial_packets.size() == 0);
  CHECK(test_state_.reassembled.access_count_ == 0);
}

TEST_F(HciPacketFragmenterTest, TwoPacket_HugeBogusSecond) {
  const size_t packet_size = kMaxPacketSize;
  const std::vector<uint8_t> data = CreateData(UINT16_MAX);
  const std::vector<uint8_t> first_part(data.cbegin(),
                                        data.cbegin() + packet_size - 1);
  reassemble_and_dispatch(AllocateL2capPacket(packet_size, first_part));

  CHECK(partial_packets.size() == 1);
  CHECK(test_state_.reassembled.access_count_ == 0);

  const std::vector<uint8_t> second_part(data.cbegin() + packet_size - 1,
                                         data.cend());
  reassemble_and_dispatch(AllocateL2capPacket(second_part));

  CHECK(partial_packets.size() == 0);
  CHECK(test_state_.reassembled.access_count_ == 1);
}
Loading