blob: 2696b0b727e80ade0655542f9f40a4210b9c124a [file] [log] [blame]
/******************************************************************************
*
* Copyright 2014 Google, Inc.
*
* 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 "hci/include/packet_fragmenter.h"
#include <gtest/gtest.h>
#include <stdint.h>
#include "device/include/controller.h"
#include "hci/include/hci_layer.h"
#include "internal_include/bt_target.h"
#include "osi/include/allocator.h"
#include "osi/include/osi.h"
#include "stack/include/bt_hdr.h"
#include "test_stubs.h"
#ifndef HCI_ISO_PREAMBLE_SIZE
#define HCI_ISO_PREAMBLE_SIZE 4
#endif
DECLARE_TEST_MODES(init, set_data_sizes, no_fragmentation, fragmentation,
ble_no_fragmentation, ble_fragmentation,
non_acl_passthrough_fragmentation, no_reassembly, reassembly,
non_acl_passthrough_reassembly, iso_no_reassembly,
iso_reassembly, iso_fragmentation, iso_no_fragmentation);
#define LOCAL_BLE_CONTROLLER_ID 1
static const char* sample_data =
"At this point they came in sight of thirty forty windmills that there are "
"on plain, and "
"as soon as Don Quixote saw them he said to his squire, \"Fortune is "
"arranging matters "
"for us better than we could have shaped our desires ourselves, for look "
"there, friend "
"Sancho Panza, where thirty or more monstrous giants present themselves, "
"all of whom I "
"mean to engage in battle and slay, and with whose spoils we shall begin "
"to make our "
"fortunes; for this is righteous warfare, and it is God's good service to "
"sweep so evil "
"a breed from off the face of the earth.\"";
static const char* small_sample_data = "\"What giants?\" said Sancho Panza.";
static const uint16_t test_iso_handle_complete_with_ts =
(0x0666 | (0x0002 << 12) | (0x0001 << 14));
static const uint16_t test_iso_handle_complete_without_ts =
(0x0666 | (0x0002 << 12));
static const uint16_t test_iso_handle_start_with_ts = (0x0666 | (0x0001 << 14));
static const uint16_t test_iso_handle_start_without_ts =
(0x0666); // Also base handle
static const uint16_t test_iso_handle_continuation = (0x0666 | (0x0001 << 12));
static const uint16_t test_iso_handle_end = (0x0666 | (0x0003 << 12));
static int packet_index;
static unsigned int data_size_sum;
static const packet_fragmenter_t* fragmenter;
static uint32_t iso_timestamp = 0x32122321;
static uint16_t iso_packet_seq = 0x1291;
static bool iso_has_ts = true;
static BT_HDR* manufacture_packet_for_fragmentation(uint16_t event,
const char* data) {
uint16_t data_length = strlen(data);
uint16_t size = data_length;
if (event == MSG_STACK_TO_HC_HCI_ISO) {
size += 8; // handle (2), length (2), packet_seq (2), sdu_len(2)
if (iso_has_ts) {
size += 4;
}
}
BT_HDR* packet = (BT_HDR*)osi_malloc(size + sizeof(BT_HDR));
packet->len = size;
packet->offset = 0;
packet->event = event;
packet->layer_specific = 0;
uint8_t* packet_data = packet->data;
if (event == MSG_STACK_TO_HC_HCI_ISO) {
if (iso_has_ts) {
packet->layer_specific |= BT_ISO_HDR_CONTAINS_TS;
UINT16_TO_STREAM(packet_data, test_iso_handle_start_with_ts);
UINT16_TO_STREAM(packet_data, data_length + 8);
UINT32_TO_STREAM(packet_data, iso_timestamp);
} else {
UINT16_TO_STREAM(packet_data, test_iso_handle_start_without_ts);
UINT16_TO_STREAM(packet_data, data_length + 4);
}
UINT16_TO_STREAM(packet_data, iso_packet_seq);
UINT16_TO_STREAM(packet_data, data_length);
}
memcpy(packet_data, data, data_length);
return packet;
}
static void expect_packet_fragmented(uint16_t event, int max_acl_data_size,
BT_HDR* packet, const char* expected_data,
bool send_complete) {
uint8_t* data = packet->data + packet->offset;
int expected_data_offset;
int length_to_check;
if (event == MSG_STACK_TO_HC_HCI_ISO) {
uint16_t handle;
uint16_t length;
STREAM_TO_UINT16(handle, data);
STREAM_TO_UINT16(length, data);
int length_remaining = strlen(expected_data) - data_size_sum;
int packet_data_length = packet->len - HCI_ISO_PREAMBLE_SIZE;
if (packet_index == 0) {
uint8_t hdr_size = 8; // ts, packet_seq, len
if (iso_has_ts) {
uint32_t timestamp;
STREAM_TO_UINT32(timestamp, data);
ASSERT_EQ(timestamp, iso_timestamp);
if (send_complete)
ASSERT_EQ(test_iso_handle_complete_with_ts, handle);
else
ASSERT_EQ(test_iso_handle_start_with_ts, handle);
} else {
if (send_complete)
ASSERT_EQ(test_iso_handle_complete_without_ts, handle);
else
ASSERT_EQ(test_iso_handle_start_without_ts, handle);
hdr_size -= 4;
}
uint16_t packet_seq;
STREAM_TO_UINT16(packet_seq, data);
ASSERT_EQ(packet_seq, iso_packet_seq);
uint16_t iso_sdu_length;
STREAM_TO_UINT16(iso_sdu_length, data);
ASSERT_EQ(iso_sdu_length, strlen(expected_data));
length_to_check = packet_data_length - hdr_size;
} else {
if (!send_complete)
ASSERT_EQ(test_iso_handle_continuation, handle);
else
ASSERT_EQ(test_iso_handle_end, handle);
length_to_check = packet_data_length;
}
if (length_remaining > max_acl_data_size)
ASSERT_EQ(max_acl_data_size, packet_data_length);
expected_data_offset = packet_index * max_acl_data_size;
if (expected_data_offset > 0) {
if (iso_has_ts) {
expected_data_offset -= 8;
} else {
expected_data_offset -= 4;
}
}
packet_index++;
} else {
length_to_check = strlen(expected_data);
expected_data_offset = 0;
}
for (int i = 0; i < length_to_check; i++) {
EXPECT_EQ(expected_data[expected_data_offset + i], data[i]);
data_size_sum++;
}
if (event == MSG_STACK_TO_HC_HCI_ISO)
EXPECT_TRUE(send_complete == (data_size_sum == strlen(expected_data)));
if (send_complete) osi_free(packet);
}
static void manufacture_iso_packet_and_then_reassemble(uint16_t event,
uint16_t iso_size,
const char* data) {
uint16_t data_length = strlen(data);
uint16_t total_length;
uint16_t length_sent = 0;
uint16_t iso_length = data_length;
BT_HDR* packet;
uint8_t* packet_data;
uint8_t hdr_size = 4; // packet seq, sdu len
// ISO length (2), packet seq (2), optional timestamp (4)
total_length = data_length + 4;
if (iso_has_ts) total_length += 4;
do {
int length_to_send = (length_sent + (iso_size - 4) < total_length)
? (iso_size - 4)
: (total_length - length_sent);
packet = (BT_HDR*)osi_malloc(length_to_send + 4 + sizeof(BT_HDR));
packet_data = packet->data;
packet->len = length_to_send + 4;
packet->offset = 0;
packet->event = event;
packet->layer_specific = 0;
bool is_complete = length_to_send == total_length;
if (length_sent == 0) { // first packet
if (iso_has_ts) {
hdr_size += 4;
if (is_complete) {
UINT16_TO_STREAM(packet_data, test_iso_handle_complete_with_ts);
} else {
UINT16_TO_STREAM(packet_data, test_iso_handle_start_with_ts);
}
} else {
if (is_complete) {
UINT16_TO_STREAM(packet_data, test_iso_handle_complete_without_ts);
} else {
UINT16_TO_STREAM(packet_data, test_iso_handle_start_without_ts);
}
}
UINT16_TO_STREAM(packet_data, length_to_send);
if (iso_has_ts) {
UINT32_TO_STREAM(packet_data, iso_timestamp);
}
UINT16_TO_STREAM(packet_data, iso_packet_seq);
UINT16_TO_STREAM(packet_data, iso_length);
memcpy(packet_data, data, length_to_send - hdr_size);
} else {
if (length_sent + length_to_send == total_length) {
UINT16_TO_STREAM(packet_data, test_iso_handle_end);
} else {
UINT16_TO_STREAM(packet_data, test_iso_handle_continuation);
}
UINT16_TO_STREAM(packet_data, length_to_send);
memcpy(packet_data, data + length_sent - hdr_size, length_to_send);
}
length_sent += length_to_send;
fragmenter->reassemble_and_dispatch(packet);
} while (length_sent < total_length);
}
static void manufacture_packet_and_then_reassemble(uint16_t event,
uint16_t packet_size,
const char* data) {
uint16_t data_length = strlen(data);
if (event == MSG_HC_TO_STACK_HCI_ISO) {
manufacture_iso_packet_and_then_reassemble(event, packet_size, data);
} else {
BT_HDR* packet = (BT_HDR*)osi_malloc(data_length + sizeof(BT_HDR));
packet->len = data_length;
packet->offset = 0;
packet->event = event;
packet->layer_specific = 0;
memcpy(packet->data, data, data_length);
fragmenter->reassemble_and_dispatch(packet);
}
}
static void expect_packet_reassembled(uint16_t event, BT_HDR* packet,
const char* expected_data) {
uint16_t expected_data_length = strlen(expected_data);
uint8_t* data = packet->data + packet->offset;
for (int i = 0; i < expected_data_length; i++) {
EXPECT_EQ(expected_data[i], data[i]);
data_size_sum++;
}
osi_free(packet);
}
static void expect_packet_reassembled_iso(uint16_t event, BT_HDR* packet,
const char* expected_data,
uint32_t expected_timestamp,
uint16_t expected_packet_seq,
bool is_complete = false) {
uint16_t expected_data_length = strlen(expected_data);
uint8_t* data = packet->data;
uint8_t hdr_size = 8;
uint16_t handle;
uint16_t length;
uint16_t iso_length;
uint32_t timestamp;
uint16_t packet_seq;
ASSERT_EQ(event, MSG_HC_TO_STACK_HCI_ISO);
STREAM_TO_UINT16(handle, data);
STREAM_TO_UINT16(length, data);
if (iso_has_ts) {
STREAM_TO_UINT32(timestamp, data);
ASSERT_TRUE((packet->layer_specific & BT_ISO_HDR_CONTAINS_TS) != 0);
ASSERT_EQ(timestamp, expected_timestamp);
ASSERT_EQ(is_complete ? test_iso_handle_complete_with_ts
: test_iso_handle_start_with_ts,
handle);
} else {
ASSERT_TRUE((packet->layer_specific & BT_ISO_HDR_CONTAINS_TS) == 0);
ASSERT_EQ(is_complete ? test_iso_handle_complete_without_ts
: test_iso_handle_start_without_ts,
handle);
hdr_size -= 4;
}
ASSERT_EQ(expected_data_length + hdr_size, length);
STREAM_TO_UINT16(packet_seq, data);
ASSERT_EQ(packet_seq, expected_packet_seq);
STREAM_TO_UINT16(iso_length, data);
ASSERT_EQ(expected_data_length, iso_length);
for (int i = 0; i < expected_data_length; i++) {
ASSERT_EQ(expected_data[i], data[i]);
data_size_sum++;
}
osi_free(packet);
}
STUB_FUNCTION(void, fragmented_callback, (BT_HDR * packet, bool send_complete))
DURING(iso_fragmentation) {
expect_packet_fragmented(MSG_STACK_TO_HC_HCI_ISO, 10, packet, sample_data,
send_complete);
return;
}
DURING(iso_no_fragmentation) {
expect_packet_fragmented(MSG_STACK_TO_HC_HCI_ISO, 42, packet,
small_sample_data, send_complete);
return;
}
DURING(non_acl_passthrough_fragmentation) AT_CALL(0) {
expect_packet_fragmented(MSG_STACK_TO_HC_HCI_CMD, 10, packet, sample_data,
send_complete);
return;
}
UNEXPECTED_CALL;
}
STUB_FUNCTION(void, reassembled_callback, (BT_HDR * packet))
DURING(iso_reassembly) AT_CALL(0) {
expect_packet_reassembled_iso(MSG_HC_TO_STACK_HCI_ISO, packet, sample_data,
iso_timestamp, iso_packet_seq);
return;
}
DURING(iso_no_reassembly) AT_CALL(0) {
expect_packet_reassembled_iso(MSG_HC_TO_STACK_HCI_ISO, packet,
small_sample_data, iso_timestamp,
iso_packet_seq, true);
return;
}
DURING(non_acl_passthrough_reassembly) AT_CALL(0) {
expect_packet_reassembled(MSG_HC_TO_STACK_HCI_EVT, packet, sample_data);
return;
}
UNEXPECTED_CALL;
}
STUB_FUNCTION(uint16_t, get_acl_data_size_classic, (void))
DURING(no_fragmentation, non_acl_passthrough_fragmentation, no_reassembly)
return 42;
DURING(fragmentation) return 10;
DURING(no_reassembly) return 1337;
UNEXPECTED_CALL;
return 0;
}
STUB_FUNCTION(uint16_t, get_acl_data_size_ble, (void))
DURING(ble_no_fragmentation) return 42;
DURING(ble_fragmentation) return 10;
UNEXPECTED_CALL;
return 0;
}
STUB_FUNCTION(uint16_t, get_iso_data_size, (void))
DURING(iso_no_fragmentation) return 42;
DURING(iso_fragmentation) return 10;
UNEXPECTED_CALL;
return 0;
}
static void reset_for(TEST_MODES_T next) {
RESET_CALL_COUNT(fragmented_callback);
RESET_CALL_COUNT(reassembled_callback);
RESET_CALL_COUNT(get_acl_data_size_classic);
RESET_CALL_COUNT(get_acl_data_size_ble);
RESET_CALL_COUNT(get_iso_data_size);
CURRENT_TEST_MODE = next;
}
class PacketFragmenterTest : public ::testing::Test {
protected:
void SetUp() override {
fragmenter =
packet_fragmenter_get_test_interface(&controller, &allocator_malloc);
packet_index = 0;
data_size_sum = 0;
callbacks.fragmented = fragmented_callback;
callbacks.reassembled = reassembled_callback;
controller.get_acl_data_size_classic = get_acl_data_size_classic;
controller.get_acl_data_size_ble = get_acl_data_size_ble;
controller.get_iso_data_size = get_iso_data_size;
reset_for(init);
fragmenter->init(&callbacks);
}
void TearDown() override { fragmenter->cleanup(); }
controller_t controller;
packet_fragmenter_callbacks_t callbacks;
};
TEST_F(PacketFragmenterTest, test_iso_fragment_necessary) {
reset_for(iso_fragmentation);
iso_has_ts = true;
BT_HDR* packet = manufacture_packet_for_fragmentation(MSG_STACK_TO_HC_HCI_ISO,
sample_data);
packet->event |= LOCAL_BLE_CONTROLLER_ID;
fragmenter->fragment_and_dispatch(packet);
ASSERT_EQ(strlen(sample_data), data_size_sum);
}
TEST_F(PacketFragmenterTest, test_iso_no_fragment_necessary) {
reset_for(iso_no_fragmentation);
iso_has_ts = true;
BT_HDR* packet = manufacture_packet_for_fragmentation(MSG_STACK_TO_HC_HCI_ISO,
small_sample_data);
packet->event |= LOCAL_BLE_CONTROLLER_ID;
fragmenter->fragment_and_dispatch(packet);
ASSERT_EQ(strlen(small_sample_data), data_size_sum);
}
TEST_F(PacketFragmenterTest, test_iso_fragment_necessary_no_ts) {
reset_for(iso_fragmentation);
iso_has_ts = false;
BT_HDR* packet = manufacture_packet_for_fragmentation(MSG_STACK_TO_HC_HCI_ISO,
sample_data);
packet->event |= LOCAL_BLE_CONTROLLER_ID;
fragmenter->fragment_and_dispatch(packet);
ASSERT_EQ(strlen(sample_data), data_size_sum);
}
TEST_F(PacketFragmenterTest, test_iso_no_fragment_necessary_no_ts) {
reset_for(iso_no_fragmentation);
iso_has_ts = false;
BT_HDR* packet = manufacture_packet_for_fragmentation(MSG_STACK_TO_HC_HCI_ISO,
small_sample_data);
packet->event |= LOCAL_BLE_CONTROLLER_ID;
fragmenter->fragment_and_dispatch(packet);
ASSERT_EQ(strlen(small_sample_data), data_size_sum);
}
TEST_F(PacketFragmenterTest, test_iso_no_reassembly_necessary) {
reset_for(iso_no_reassembly);
iso_has_ts = true;
manufacture_packet_and_then_reassemble(MSG_HC_TO_STACK_HCI_ISO, 50,
small_sample_data);
ASSERT_EQ(strlen(small_sample_data), data_size_sum);
EXPECT_CALL_COUNT(reassembled_callback, 1);
}
TEST_F(PacketFragmenterTest, test_iso_reassembly_necessary) {
reset_for(iso_reassembly);
iso_has_ts = true;
manufacture_packet_and_then_reassemble(MSG_HC_TO_STACK_HCI_ISO, 42,
sample_data);
ASSERT_EQ(strlen(sample_data), data_size_sum);
EXPECT_CALL_COUNT(reassembled_callback, 1);
}
TEST_F(PacketFragmenterTest, test_iso_no_reassembly_necessary_no_ts) {
reset_for(iso_no_reassembly);
iso_has_ts = false;
manufacture_packet_and_then_reassemble(MSG_HC_TO_STACK_HCI_ISO, (42 + 4),
small_sample_data);
ASSERT_EQ(strlen(small_sample_data), data_size_sum);
EXPECT_CALL_COUNT(reassembled_callback, 1);
}
TEST_F(PacketFragmenterTest, test_iso_reassembly_necessary_no_ts) {
reset_for(iso_reassembly);
iso_has_ts = false;
manufacture_packet_and_then_reassemble(MSG_HC_TO_STACK_HCI_ISO, 42,
sample_data);
ASSERT_EQ(strlen(sample_data), data_size_sum);
EXPECT_CALL_COUNT(reassembled_callback, 1);
}