blob: b166e85e48c2cbb059d0f0036ebd90436c43a169 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/connectivity/bluetooth/core/bt-host/att/bearer.h"
#include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/fake_channel_test.h"
namespace bt::att {
namespace {
constexpr OpCode kTestRequest = kFindInformationRequest;
constexpr OpCode kTestResponse = kFindInformationResponse;
constexpr OpCode kTestRequest2 = kExchangeMTURequest;
constexpr OpCode kTestResponse2 = kExchangeMTUResponse;
constexpr OpCode kTestRequest3 = kFindByTypeValueRequest;
constexpr OpCode kTestResponse3 = kFindByTypeValueResponse;
constexpr OpCode kTestCommand = kWriteCommand;
void NopHandler(Bearer::TransactionId /*unused*/, const PacketReader& /*unused*/) {}
class BearerTest : public l2cap::testing::FakeChannelTest {
public:
BearerTest() = default;
~BearerTest() override = default;
protected:
void SetUp() override {
ChannelOptions options(l2cap::kATTChannelId);
fake_att_chan_ = CreateFakeChannel(options);
bearer_ = Bearer::Create(fake_att_chan_);
}
void TearDown() override { bearer_ = nullptr; }
Bearer* bearer() const { return bearer_.get(); }
l2cap::testing::FakeChannel* fake_att_chan() const { return fake_att_chan_.get(); }
void DeleteBearer() { bearer_ = nullptr; }
private:
fbl::RefPtr<l2cap::testing::FakeChannel> fake_att_chan_;
fbl::RefPtr<Bearer> bearer_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(BearerTest);
};
TEST_F(BearerTest, CreateFailsToActivate) {
ChannelOptions options(l2cap::kATTChannelId);
auto fake_chan = CreateFakeChannel(options);
fake_chan->set_activate_fails(true);
EXPECT_FALSE(Bearer::Create(std::move(fake_chan)));
}
TEST_F(BearerTest, CreateUsesLEMaxMTUAsPreferredMTU) {
EXPECT_EQ(kLEMaxMTU, bearer()->preferred_mtu());
}
TEST_F(BearerTest, ShutDown) {
ASSERT_TRUE(bearer()->is_open());
ASSERT_FALSE(fake_att_chan()->link_error());
// Verify that shutting down an open bearer notifies the closed callback.
bool called = false;
auto cb = [&called] { called = true; };
bearer()->set_closed_callback(cb);
bearer()->ShutDown();
EXPECT_TRUE(called);
EXPECT_FALSE(bearer()->is_open());
// Bearer should also signal a link error over the channel.
EXPECT_TRUE(fake_att_chan()->link_error());
// ShutDown() on a closed bearer does nothing.
bearer()->ShutDown();
EXPECT_FALSE(bearer()->is_open());
}
TEST_F(BearerTest, StartTransactionErrorClosed) {
bearer()->ShutDown();
ASSERT_FALSE(bearer()->is_open());
bool received_error = false;
Bearer::TransactionCallback cb = [&received_error](Bearer::TransactionResult result) {
if (result.is_error()) {
received_error = true;
}
};
bearer()->StartTransaction(NewBuffer(kTestRequest), std::move(cb));
EXPECT_TRUE(received_error);
}
TEST_F(BearerTest, StartTransactionInvalidPacket) {
auto cb = [](Bearer::TransactionResult) { FAIL(); };
// Empty
EXPECT_DEATH_IF_SUPPORTED(bearer()->StartTransaction(std::make_unique<BufferView>(), cb),
"bad length");
// Exceeds MTU.
bearer()->set_mtu(1);
EXPECT_DEATH_IF_SUPPORTED(bearer()->StartTransaction(NewBuffer(kTestRequest, 2), cb),
"bad length");
}
TEST_F(BearerTest, StartTransactionWrongMethodType) {
auto cb = [](Bearer::TransactionResult) { FAIL(); };
// Command
EXPECT_DEATH_IF_SUPPORTED(bearer()->StartTransaction(NewBuffer(kWriteCommand), cb),
"callback was provided");
// Notification
EXPECT_DEATH_IF_SUPPORTED(bearer()->StartTransaction(NewBuffer(kNotification), cb),
"callback was provided");
}
TEST_F(BearerTest, RequestTimeout) {
// We expect the channel to be closed and the pending transaction to end in an
// error.
bool closed = false;
bool err_cb_called = false;
bearer()->set_closed_callback([&closed] { closed = true; });
auto cb = [&err_cb_called](Bearer::TransactionResult result) {
if (result.is_ok()) {
return;
}
const auto& [error, handle] = result.error_value();
EXPECT_EQ(Error(HostError::kTimedOut), error);
EXPECT_EQ(0, handle);
err_cb_called = true;
};
ASSERT_FALSE(fake_att_chan()->link_error());
bearer()->StartTransaction(NewBuffer(kTestRequest), cb);
RunLoopFor(kTransactionTimeout);
EXPECT_TRUE(closed);
EXPECT_TRUE(err_cb_called);
EXPECT_TRUE(fake_att_chan()->link_error());
}
// Queue many requests but make sure that FakeChannel only receives one.
TEST_F(BearerTest, RequestTimeoutMany) {
constexpr unsigned int kTransactionCount = 2;
unsigned int chan_count = 0;
auto chan_cb = [&chan_count](auto cb_packet) {
chan_count++;
// This should only be called once and for the first request.
EXPECT_EQ(kTestRequest, (*cb_packet)[0]);
};
fake_chan()->SetSendCallback(chan_cb, dispatcher());
bool closed = false;
unsigned int err_cb_count = 0u;
bearer()->set_closed_callback([&closed] { closed = true; });
auto cb = [&err_cb_count](Bearer::TransactionResult result) {
if (result.is_ok()) {
return;
}
const auto& [error, handle] = result.error_value();
EXPECT_EQ(Error(HostError::kTimedOut), error);
EXPECT_EQ(0, handle);
err_cb_count++;
};
bearer()->StartTransaction(NewBuffer(kTestRequest, 'T', 'e', 's', 't'), cb);
bearer()->StartTransaction(NewBuffer(kTestRequest2, 'T', 'e', 's', 't'), cb);
RunLoopUntilIdle();
// The first indication should have been sent and the second one queued.
EXPECT_EQ(1u, chan_count);
EXPECT_FALSE(closed);
EXPECT_EQ(0u, err_cb_count);
// Make the request timeout.
RunLoopFor(kTransactionTimeout);
EXPECT_TRUE(closed);
EXPECT_EQ(kTransactionCount, err_cb_count);
}
TEST_F(BearerTest, IndicationTimeout) {
// We expect the channel to be closed and the pending transaction to end in an
// error.
bool closed = false;
bool err_cb_called = false;
bearer()->set_closed_callback([&closed] { closed = true; });
auto cb = [&err_cb_called](Bearer::TransactionResult result) {
if (result.is_ok()) {
return;
}
const auto& [error, handle] = result.error_value();
EXPECT_EQ(Error(HostError::kTimedOut), error);
EXPECT_EQ(0, handle);
err_cb_called = true;
};
bearer()->StartTransaction(NewBuffer(kIndication, 'T', 'e', 's', 't'), cb);
RunLoopFor(kTransactionTimeout);
EXPECT_TRUE(closed);
EXPECT_TRUE(err_cb_called);
}
// Queue many indications but make sure that FakeChannel only receives one.
TEST_F(BearerTest, IndicationTimeoutMany) {
constexpr unsigned int kTransactionCount = 2;
constexpr uint8_t kIndValue1 = 1;
constexpr uint8_t kIndValue2 = 2;
unsigned int chan_count = 0;
auto chan_cb = [kIndValue1, &chan_count](auto cb_packet) {
chan_count++;
// This should only be called once and for the first request.
EXPECT_EQ(kIndValue1, (*cb_packet)[1]);
};
fake_chan()->SetSendCallback(chan_cb, dispatcher());
bool closed = false;
unsigned int err_cb_count = 0u;
bearer()->set_closed_callback([&closed] { closed = true; });
auto cb = [&err_cb_count](Bearer::TransactionResult result) {
if (result.is_ok()) {
return;
}
const auto& [error, handle] = result.error_value();
EXPECT_EQ(Error(HostError::kTimedOut), error);
EXPECT_EQ(0, handle);
err_cb_count++;
};
bearer()->StartTransaction(NewBuffer(kIndication, kIndValue1), cb);
bearer()->StartTransaction(NewBuffer(kIndication, kIndValue2), cb);
RunLoopUntilIdle();
// The first indication should have been sent and the second one queued.
EXPECT_EQ(1u, chan_count);
EXPECT_FALSE(closed);
EXPECT_EQ(0u, err_cb_count);
// Make the request timeout.
RunLoopFor(kTransactionTimeout);
EXPECT_TRUE(closed);
EXPECT_EQ(kTransactionCount, err_cb_count);
}
TEST_F(BearerTest, ReceiveEmptyPacket) {
bool closed = false;
bearer()->set_closed_callback([&closed] { closed = true; });
fake_chan()->Receive(BufferView());
RunLoopUntilIdle();
EXPECT_TRUE(closed);
}
TEST_F(BearerTest, ReceiveResponseWithoutRequest) {
bool closed = false;
bearer()->set_closed_callback([&closed] { closed = true; });
fake_chan()->Receive(StaticByteBuffer(kTestResponse));
RunLoopUntilIdle();
EXPECT_TRUE(closed);
}
TEST_F(BearerTest, ReceiveConfirmationWithoutIndication) {
bool closed = false;
bearer()->set_closed_callback([&closed] { closed = true; });
fake_chan()->Receive(StaticByteBuffer(kConfirmation));
RunLoopUntilIdle();
EXPECT_TRUE(closed);
}
TEST_F(BearerTest, SendRequestWrongResponse) {
unsigned int count = 0;
auto chan_cb = [this, &count](auto cb_packet) {
count++;
// This should only be called once and for the first request.
EXPECT_EQ(kTestRequest, (*cb_packet)[0]);
// Send back the wrong response.
fake_chan()->Receive(StaticByteBuffer(kTestResponse2));
};
fake_chan()->SetSendCallback(chan_cb, dispatcher());
bool err_cb_called = false;
bool closed = false;
bearer()->set_closed_callback([&closed] { closed = true; });
auto cb = [&err_cb_called](Bearer::TransactionResult result) {
if (result.is_ok()) {
return;
}
const auto& [error, handle] = result.error_value();
EXPECT_EQ(Error(HostError::kFailed), error);
EXPECT_EQ(0, handle);
err_cb_called = true;
};
bearer()->StartTransaction(NewBuffer(kTestRequest), cb);
RunLoopUntilIdle();
EXPECT_TRUE(closed);
EXPECT_TRUE(err_cb_called);
EXPECT_EQ(1u, count);
}
TEST_F(BearerTest, SendRequestErrorResponseTooShort) {
StaticByteBuffer malformed_error_rsp(
// Opcode: error response
kErrorResponse,
// Parameters are too short (by 1 byte). Contents are unimportant, as the
// PDU should be rejected.
1, 2, 3);
bool chan_cb_called = false;
auto chan_cb = [this, &chan_cb_called, &malformed_error_rsp](auto cb_packet) {
ASSERT_FALSE(chan_cb_called);
EXPECT_EQ(kTestRequest, (*cb_packet)[0]);
chan_cb_called = true;
fake_chan()->Receive(malformed_error_rsp);
};
fake_chan()->SetSendCallback(chan_cb, dispatcher());
bool err_cb_called = false;
bool closed = false;
bearer()->set_closed_callback([&closed] { closed = true; });
auto cb = [&err_cb_called](Bearer::TransactionResult result) {
if (result.is_ok()) {
return;
}
const auto& [error, handle] = result.error_value();
EXPECT_EQ(Error(HostError::kFailed), error);
EXPECT_EQ(0, handle);
err_cb_called = true;
};
bearer()->StartTransaction(NewBuffer(kTestRequest), cb);
RunLoopUntilIdle();
EXPECT_TRUE(closed);
EXPECT_TRUE(err_cb_called);
EXPECT_TRUE(chan_cb_called);
}
TEST_F(BearerTest, SendRequestErrorResponseTooLong) {
StaticByteBuffer malformed_error_rsp(
// Opcode: error response
kErrorResponse,
// Parameters are too long (by 1 byte). Contents are unimportant, as the
// PDU should be rejected.
1, 2, 3, 4, 5);
bool chan_cb_called = false;
auto chan_cb = [this, &chan_cb_called, &malformed_error_rsp](auto cb_packet) {
ASSERT_FALSE(chan_cb_called);
EXPECT_EQ(kTestRequest, (*cb_packet)[0]);
chan_cb_called = true;
fake_chan()->Receive(malformed_error_rsp);
};
fake_chan()->SetSendCallback(chan_cb, dispatcher());
bool err_cb_called = false;
bool closed = false;
bearer()->set_closed_callback([&closed] { closed = true; });
auto cb = [&err_cb_called](Bearer::TransactionResult result) {
if (result.is_ok()) {
return;
}
const auto& [error, handle] = result.error_value();
EXPECT_EQ(Error(HostError::kFailed), error);
EXPECT_EQ(0, handle);
err_cb_called = true;
};
bearer()->StartTransaction(NewBuffer(kTestRequest), cb);
RunLoopUntilIdle();
EXPECT_TRUE(closed);
EXPECT_TRUE(err_cb_called);
EXPECT_TRUE(chan_cb_called);
}
TEST_F(BearerTest, SendRequestErrorResponseWrongOpCode) {
StaticByteBuffer error_rsp(
// Opcode: error response
kErrorResponse,
// request opcode: non-matching opcode in error response
kTestRequest2,
// handle, should be ignored
0x00, 0x00,
// error code:
ErrorCode::kRequestNotSupported);
bool chan_cb_called = false;
auto chan_cb = [this, &chan_cb_called, &error_rsp](auto cb_packet) {
ASSERT_FALSE(chan_cb_called);
EXPECT_EQ(kTestRequest, (*cb_packet)[0]);
chan_cb_called = true;
fake_chan()->Receive(error_rsp);
};
fake_chan()->SetSendCallback(chan_cb, dispatcher());
bool err_cb_called = false;
bool closed = false;
bearer()->set_closed_callback([&closed] { closed = true; });
auto cb = [&err_cb_called](Bearer::TransactionResult result) {
if (result.is_ok()) {
return;
}
const auto& [error, handle] = result.error_value();
EXPECT_EQ(Error(HostError::kFailed), error);
EXPECT_EQ(0, handle);
err_cb_called = true;
};
bearer()->StartTransaction(NewBuffer(kTestRequest), cb);
RunLoopUntilIdle();
EXPECT_TRUE(closed);
EXPECT_TRUE(err_cb_called);
EXPECT_TRUE(chan_cb_called);
}
TEST_F(BearerTest, SendRequestErrorResponse) {
StaticByteBuffer error_rsp(
// Opcode: error response
kErrorResponse,
// request opcode
kTestRequest,
// handle (0x0001)
0x01, 0x00,
// error code:
ErrorCode::kRequestNotSupported);
bool chan_cb_called = false;
auto chan_cb = [this, &chan_cb_called, &error_rsp](auto cb_packet) {
ASSERT_FALSE(chan_cb_called);
EXPECT_EQ(kTestRequest, (*cb_packet)[0]);
chan_cb_called = true;
fake_chan()->Receive(error_rsp);
};
fake_chan()->SetSendCallback(chan_cb, dispatcher());
bool err_cb_called = false;
auto cb = [&err_cb_called](Bearer::TransactionResult result) {
if (result.is_ok()) {
return;
}
const auto& [error, handle] = result.error_value();
EXPECT_EQ(Error(ErrorCode::kRequestNotSupported), error);
EXPECT_EQ(0x0001, handle);
err_cb_called = true;
};
bearer()->StartTransaction(NewBuffer(kTestRequest), cb);
RunLoopUntilIdle();
EXPECT_TRUE(err_cb_called);
EXPECT_TRUE(chan_cb_called);
// The channel should remain open
EXPECT_TRUE(bearer()->is_open());
}
TEST_F(BearerTest, SendRequestSuccess) {
StaticByteBuffer response(kTestResponse, 'T', 'e', 's', 't');
bool chan_cb_called = false;
auto chan_cb = [this, &chan_cb_called, &response](auto cb_packet) {
ASSERT_FALSE(chan_cb_called);
EXPECT_EQ(kTestRequest, (*cb_packet)[0]);
chan_cb_called = true;
fake_chan()->Receive(response);
};
fake_chan()->SetSendCallback(chan_cb, dispatcher());
bool cb_called = false;
auto cb = [&cb_called, &response](Bearer::TransactionResult result) {
ASSERT_FALSE(cb_called);
if (result.is_error()) {
return;
}
cb_called = true;
EXPECT_TRUE(ContainersEqual(response, result.value().data()));
};
bearer()->StartTransaction(NewBuffer(kTestRequest), cb);
RunLoopUntilIdle();
EXPECT_TRUE(chan_cb_called);
EXPECT_TRUE(cb_called);
// The channel should remain open
EXPECT_TRUE(bearer()->is_open());
}
// Closing the L2CAP channel while ATT requests have been queued will cause the
// error callbacks to be called. The code should fail gracefully if one of these
// callbacks deletes the attribute bearer.
TEST_F(BearerTest, CloseChannelAndDeleteBearerWhileRequestsArePending) {
// We expect the callback to be called 3 times since we queue 3 transactions
// below.
constexpr size_t kExpectedCount = 3;
size_t cb_error_count = 0;
auto cb = [this, &cb_error_count](Bearer::TransactionResult result) {
if (result.is_ok()) {
return;
}
cb_error_count++;
// Delete the bearer on the first callback. The remaining callbacks should
// still run gracefully.
DeleteBearer();
};
bearer()->StartTransaction(NewBuffer(kTestRequest), cb);
bearer()->StartTransaction(NewBuffer(kTestRequest), cb);
bearer()->StartTransaction(NewBuffer(kTestRequest), cb);
fake_chan()->Close();
EXPECT_EQ(kExpectedCount, cb_error_count);
}
TEST_F(BearerTest, SendManyRequests) {
const StaticByteBuffer response1(kTestResponse, 'f', 'o', 'o');
const StaticByteBuffer response2(kErrorResponse,
// request opcode
kTestRequest2,
// handle (0x0001)
0x01, 0x00,
// error code:
ErrorCode::kRequestNotSupported);
const StaticByteBuffer response3(kTestResponse3, 'b', 'a', 'r');
auto chan_cb = [&, this](auto cb_packet) {
OpCode opcode = (*cb_packet)[0];
if (opcode == kTestRequest) {
fake_chan()->Receive(response1);
} else if (opcode == kTestRequest2) {
fake_chan()->Receive(response2);
} else if (opcode == kTestRequest3) {
fake_chan()->Receive(response3);
}
};
fake_chan()->SetSendCallback(chan_cb, dispatcher());
bool called_1 = false, called_2 = false, called_3 = false;
// We expect each callback to be called in the order that we send the corresponding request.
auto callback1 = [&called_1, &called_2, &called_3, &response1](Bearer::TransactionResult result) {
EXPECT_FALSE(called_2);
EXPECT_FALSE(called_3);
called_1 = true;
// First request should've succeeded
ASSERT_EQ(fitx::ok(), result);
EXPECT_TRUE(ContainersEqual(response1, result.value().data()));
};
bearer()->StartTransaction(NewBuffer(kTestRequest), callback1);
auto callback2 = [&called_1, &called_2, &called_3](Bearer::TransactionResult result) {
EXPECT_TRUE(called_1);
EXPECT_FALSE(called_3);
called_2 = true;
// Second request should've failed
ASSERT_EQ(fitx::failed(), result);
const auto& [error, handle] = result.error_value();
EXPECT_EQ(Error(ErrorCode::kRequestNotSupported), error);
EXPECT_EQ(0x0001, handle);
};
bearer()->StartTransaction(NewBuffer(kTestRequest2), callback2);
auto callback3 = [&called_1, &called_2, &called_3, &response3](Bearer::TransactionResult result) {
EXPECT_TRUE(called_1);
EXPECT_TRUE(called_2);
called_3 = true;
// Third request should've succeeded
ASSERT_EQ(fitx::ok(), result);
EXPECT_TRUE(ContainersEqual(response3, result.value().data()));
};
bearer()->StartTransaction(NewBuffer(kTestRequest3), callback3);
RunLoopUntilIdle();
EXPECT_TRUE(bearer()->is_open());
}
// An indication transaction can only fail in a circumstance that would shut
// down the bearer (e.g. a transaction timeout or an empty PDU). Otherwise,
// Bearer will only complete an indication transaction when it receives a
// confirmation PDU.
//
// NOTE: Bearer only looks at the opcode of a PDU and ignores the payload, so a
// malformed confirmation payload is not considered an error at this layer.
TEST_F(BearerTest, SendIndicationSuccess) {
// Even though this is a malformed confirmation PDU it will not be rejected by
// Bearer.
StaticByteBuffer conf(kConfirmation, 'T', 'e', 's', 't');
bool chan_cb_called = false;
auto chan_cb = [this, &chan_cb_called, &conf](auto cb_packet) {
ASSERT_FALSE(chan_cb_called);
EXPECT_EQ(kIndication, (*cb_packet)[0]);
chan_cb_called = true;
fake_chan()->Receive(conf);
};
fake_chan()->SetSendCallback(chan_cb, dispatcher());
bool cb_called = false;
auto cb = [&cb_called, &conf](Bearer::TransactionResult result) {
ASSERT_FALSE(cb_called);
cb_called = true;
EXPECT_TRUE(ContainersEqual(conf, result.value().data()));
};
bearer()->StartTransaction(NewBuffer(kIndication), cb);
RunLoopUntilIdle();
EXPECT_TRUE(chan_cb_called);
EXPECT_TRUE(cb_called);
// The channel should remain open
EXPECT_TRUE(bearer()->is_open());
}
TEST_F(BearerTest, SendWithoutResponseErrorClosed) {
bearer()->ShutDown();
ASSERT_FALSE(bearer()->is_open());
EXPECT_FALSE(bearer()->SendWithoutResponse(NewBuffer(kTestCommand)));
}
TEST_F(BearerTest, SendWithoutResponseInvalidPacket) {
// Empty
EXPECT_DEATH_IF_SUPPORTED(
[[maybe_unused]] auto _ = bearer()->SendWithoutResponse(std::make_unique<BufferView>()),
"bad length");
// Exceeds MTU
bearer()->set_mtu(1);
EXPECT_DEATH_IF_SUPPORTED(
[[maybe_unused]] auto _ = bearer()->SendWithoutResponse(NewBuffer(kTestCommand, 2)),
"bad length");
}
TEST_F(BearerTest, SendWithoutResponseWrongMethodType) {
EXPECT_DEATH_IF_SUPPORTED(
[[maybe_unused]] auto _ = bearer()->SendWithoutResponse(NewBuffer(kTestRequest)),
"requires callback");
EXPECT_DEATH_IF_SUPPORTED(
[[maybe_unused]] auto _ = bearer()->SendWithoutResponse(NewBuffer(kTestResponse)),
"unsupported opcode");
EXPECT_DEATH_IF_SUPPORTED(
[[maybe_unused]] auto _ = bearer()->SendWithoutResponse(NewBuffer(kIndication)),
"requires callback");
}
TEST_F(BearerTest, SendWithoutResponseCorrectMethodType) {
EXPECT_TRUE(bearer()->SendWithoutResponse(NewBuffer(kNotification)));
EXPECT_TRUE(bearer()->SendWithoutResponse(NewBuffer(kTestCommand)));
EXPECT_TRUE(bearer()->SendWithoutResponse(NewBuffer(kTestRequest | kCommandFlag)));
// Any opcode is accepted as long as it has the command flag set.
EXPECT_TRUE(bearer()->SendWithoutResponse(NewBuffer(kInvalidOpCode | kCommandFlag)));
}
TEST_F(BearerTest, SendWithoutResponseMany) {
// Everything should go through without any flow control.
constexpr unsigned int kExpectedCount = 10;
unsigned int chan_cb_count = 0u;
auto chan_cb = [&chan_cb_count](auto cb_packet) {
OpCode opcode = (*cb_packet)[0];
EXPECT_TRUE(kCommandFlag & opcode || opcode == kIndication);
chan_cb_count++;
};
fake_chan()->SetSendCallback(chan_cb, dispatcher());
for (OpCode opcode = 0; opcode < kExpectedCount; opcode++) {
// Everything
EXPECT_TRUE(bearer()->SendWithoutResponse(NewBuffer(opcode | kCommandFlag)));
}
RunLoopUntilIdle();
EXPECT_EQ(kExpectedCount, chan_cb_count);
}
TEST_F(BearerTest, RegisterHandlerErrorClosed) {
bearer()->ShutDown();
EXPECT_FALSE(bearer()->is_open());
EXPECT_EQ(Bearer::kInvalidHandlerId, bearer()->RegisterHandler(kWriteRequest, NopHandler));
EXPECT_EQ(Bearer::kInvalidHandlerId, bearer()->RegisterHandler(kIndication, NopHandler));
}
TEST_F(BearerTest, RegisterHandlerErrorAlreadyRegistered) {
EXPECT_NE(Bearer::kInvalidHandlerId, bearer()->RegisterHandler(kIndication, NopHandler));
EXPECT_EQ(Bearer::kInvalidHandlerId, bearer()->RegisterHandler(kIndication, NopHandler));
}
TEST_F(BearerTest, UnregisterHandler) {
auto id0 = bearer()->RegisterHandler(kNotification, NopHandler);
EXPECT_NE(Bearer::kInvalidHandlerId, id0);
bearer()->UnregisterHandler(id0);
// It should be possible to register new handlers for the same opcodes.
id0 = bearer()->RegisterHandler(kNotification, NopHandler);
EXPECT_NE(Bearer::kInvalidHandlerId, id0);
}
TEST_F(BearerTest, RemoteTransactionNoHandler) {
StaticByteBuffer error_rsp(
// opcode
kErrorResponse,
// request opcode
kTestRequest,
// handle
0x00, 0x00,
// error code
ErrorCode::kRequestNotSupported);
bool received_error_rsp = false;
auto chan_cb = [&received_error_rsp, &error_rsp](auto packet) {
received_error_rsp = true;
EXPECT_TRUE(ContainersEqual(error_rsp, *packet));
};
fake_chan()->SetSendCallback(chan_cb, dispatcher());
fake_chan()->Receive(StaticByteBuffer(kTestRequest));
RunLoopUntilIdle();
EXPECT_TRUE(received_error_rsp);
}
TEST_F(BearerTest, RemoteTransactionSeqProtocolError) {
int request_count = 0;
auto handler = [&request_count](auto id, const PacketReader& packet) {
EXPECT_EQ(kTestRequest, packet.opcode());
EXPECT_EQ(0u, packet.payload_size());
request_count++;
};
bearer()->RegisterHandler(kTestRequest, handler);
fake_chan()->Receive(StaticByteBuffer(kTestRequest));
RunLoopUntilIdle();
ASSERT_EQ(1, request_count);
// Receiving a second request before sending a response should close the
// bearer.
bool closed = false;
bearer()->set_closed_callback([&closed] { closed = true; });
fake_chan()->Receive(StaticByteBuffer(kTestRequest));
RunLoopUntilIdle();
EXPECT_TRUE(closed);
EXPECT_EQ(1, request_count);
EXPECT_FALSE(bearer()->is_open());
}
TEST_F(BearerTest, RemoteIndicationSeqProtocolError) {
int ind_count = 0;
auto handler = [&ind_count](auto id, const PacketReader& packet) {
EXPECT_EQ(kIndication, packet.opcode());
EXPECT_EQ(0u, packet.payload_size());
ind_count++;
};
bearer()->RegisterHandler(kIndication, handler);
fake_chan()->Receive(StaticByteBuffer(kIndication));
RunLoopUntilIdle();
ASSERT_EQ(1, ind_count);
// Receiving a second indication before sending a confirmation should close
// the bearer.
bool closed = false;
bearer()->set_closed_callback([&closed] { closed = true; });
fake_chan()->Receive(StaticByteBuffer(kIndication));
RunLoopUntilIdle();
EXPECT_TRUE(closed);
EXPECT_EQ(1, ind_count);
EXPECT_FALSE(bearer()->is_open());
}
TEST_F(BearerTest, ReplyInvalidPacket) {
// Empty
EXPECT_FALSE(bearer()->Reply(0, std::make_unique<BufferView>()));
// Exceeds MTU.
bearer()->set_mtu(1);
EXPECT_FALSE(bearer()->Reply(0, NewBuffer(kTestRequest, 2)));
}
TEST_F(BearerTest, ReplyInvalidId) {
EXPECT_FALSE(bearer()->Reply(Bearer::kInvalidTransactionId, NewBuffer(kTestResponse)));
// The ID is valid but doesn't correspond to an active transaction.
EXPECT_FALSE(bearer()->Reply(1u, NewBuffer(kTestResponse)));
}
TEST_F(BearerTest, ReplyWrongOpCode) {
Bearer::TransactionId id;
bool handler_called = false;
auto handler = [&id, &handler_called](auto cb_id, const PacketReader& packet) {
EXPECT_EQ(kTestRequest, packet.opcode());
EXPECT_EQ(0u, packet.payload_size());
handler_called = true;
id = cb_id;
};
bearer()->RegisterHandler(kTestRequest, handler);
fake_chan()->Receive(StaticByteBuffer(kTestRequest));
RunLoopUntilIdle();
ASSERT_TRUE(handler_called);
EXPECT_FALSE(bearer()->Reply(id, NewBuffer(kTestResponse2)));
}
TEST_F(BearerTest, ReplyToIndicationWrongOpCode) {
Bearer::TransactionId id;
bool handler_called = false;
auto handler = [&id, &handler_called](auto cb_id, const PacketReader& packet) {
EXPECT_EQ(kIndication, packet.opcode());
EXPECT_EQ(0u, packet.payload_size());
handler_called = true;
id = cb_id;
};
bearer()->RegisterHandler(kIndication, handler);
fake_chan()->Receive(StaticByteBuffer(kIndication));
RunLoopUntilIdle();
ASSERT_TRUE(handler_called);
EXPECT_FALSE(bearer()->Reply(id, NewBuffer(kTestResponse)));
}
TEST_F(BearerTest, ReplyWithResponse) {
bool response_sent = false;
auto chan_cb = [&response_sent](auto packet) {
response_sent = true;
EXPECT_EQ(kTestResponse, (*packet)[0]);
};
fake_chan()->SetSendCallback(chan_cb, dispatcher());
Bearer::TransactionId id;
bool handler_called = false;
auto handler = [&id, &handler_called](auto cb_id, const PacketReader& packet) {
EXPECT_EQ(kTestRequest, packet.opcode());
EXPECT_EQ(0u, packet.payload_size());
handler_called = true;
id = cb_id;
};
bearer()->RegisterHandler(kTestRequest, handler);
fake_chan()->Receive(StaticByteBuffer(kTestRequest));
RunLoopUntilIdle();
ASSERT_TRUE(handler_called);
EXPECT_TRUE(bearer()->Reply(id, NewBuffer(kTestResponse)));
// The transaction is marked as complete.
EXPECT_FALSE(bearer()->Reply(id, NewBuffer(kTestResponse)));
EXPECT_FALSE(bearer()->ReplyWithError(id, 0, ErrorCode::kUnlikelyError));
RunLoopUntilIdle();
EXPECT_TRUE(response_sent);
}
TEST_F(BearerTest, IndicationConfirmation) {
bool conf_sent = false;
auto chan_cb = [&conf_sent](auto packet) {
conf_sent = true;
EXPECT_EQ(kConfirmation, (*packet)[0]);
};
fake_chan()->SetSendCallback(chan_cb, dispatcher());
Bearer::TransactionId id;
bool handler_called = false;
auto handler = [&id, &handler_called](auto cb_id, const PacketReader& packet) {
EXPECT_EQ(kIndication, packet.opcode());
EXPECT_EQ(0u, packet.payload_size());
handler_called = true;
id = cb_id;
};
bearer()->RegisterHandler(kIndication, handler);
fake_chan()->Receive(StaticByteBuffer(kIndication));
RunLoopUntilIdle();
ASSERT_TRUE(handler_called);
EXPECT_TRUE(bearer()->Reply(id, NewBuffer(kConfirmation)));
// The transaction is marked as complete.
EXPECT_FALSE(bearer()->Reply(id, NewBuffer(kConfirmation)));
RunLoopUntilIdle();
EXPECT_TRUE(conf_sent);
}
TEST_F(BearerTest, ReplyWithErrorInvalidId) {
EXPECT_FALSE(bearer()->ReplyWithError(0, 0, ErrorCode::kUnlikelyError));
}
TEST_F(BearerTest, IndicationReplyWithError) {
Bearer::TransactionId id;
bool handler_called = false;
auto handler = [&id, &handler_called](auto cb_id, const PacketReader& packet) {
EXPECT_EQ(kIndication, packet.opcode());
EXPECT_EQ(0u, packet.payload_size());
handler_called = true;
id = cb_id;
};
bearer()->RegisterHandler(kIndication, handler);
fake_chan()->Receive(StaticByteBuffer(kIndication));
RunLoopUntilIdle();
ASSERT_TRUE(handler_called);
// Cannot reply to an indication with error.
EXPECT_FALSE(bearer()->ReplyWithError(id, 0, ErrorCode::kUnlikelyError));
}
TEST_F(BearerTest, ReplyWithError) {
bool response_sent = false;
auto chan_cb = [&response_sent](auto packet) {
response_sent = true;
// The error response that we send below
StaticByteBuffer expected(kErrorResponse, kTestRequest, 0x00, 0x00, ErrorCode::kUnlikelyError);
EXPECT_TRUE(ContainersEqual(expected, *packet));
};
fake_chan()->SetSendCallback(chan_cb, dispatcher());
Bearer::TransactionId id;
bool handler_called = false;
auto handler = [&id, &handler_called](auto cb_id, const PacketReader& packet) {
EXPECT_EQ(kTestRequest, packet.opcode());
EXPECT_EQ(0u, packet.payload_size());
handler_called = true;
id = cb_id;
};
bearer()->RegisterHandler(kTestRequest, handler);
fake_chan()->Receive(StaticByteBuffer(kTestRequest));
RunLoopUntilIdle();
ASSERT_TRUE(handler_called);
EXPECT_TRUE(bearer()->ReplyWithError(id, 0, ErrorCode::kUnlikelyError));
// The transaction is marked as complete.
EXPECT_FALSE(bearer()->Reply(id, NewBuffer(kTestResponse)));
EXPECT_FALSE(bearer()->ReplyWithError(id, 0, ErrorCode::kUnlikelyError));
RunLoopUntilIdle();
EXPECT_TRUE(response_sent);
}
// Requests and indications have independent flow control
TEST_F(BearerTest, RequestAndIndication) {
Bearer::TransactionId req_id, ind_id;
int req_count = 0;
int ind_count = 0;
auto req_handler = [&req_id, &req_count](auto id, const auto& packet) {
EXPECT_EQ(kTestRequest, packet.opcode());
EXPECT_EQ(0u, packet.payload_size());
req_count++;
req_id = id;
};
auto ind_handler = [&ind_id, &ind_count](auto id, const auto& packet) {
EXPECT_EQ(kIndication, packet.opcode());
EXPECT_EQ(0u, packet.payload_size());
ind_count++;
ind_id = id;
};
bearer()->RegisterHandler(kTestRequest, req_handler);
bearer()->RegisterHandler(kIndication, ind_handler);
fake_chan()->Receive(StaticByteBuffer(kTestRequest));
fake_chan()->Receive(StaticByteBuffer(kIndication));
RunLoopUntilIdle();
EXPECT_EQ(1, req_count);
ASSERT_EQ(1, ind_count);
// Opcodes for the wrong transaction should be rejected.
EXPECT_FALSE(bearer()->Reply(ind_id, NewBuffer(kTestResponse)));
EXPECT_FALSE(bearer()->Reply(req_id, NewBuffer(kConfirmation)));
// It should be possible to end two distinct transactions.
EXPECT_TRUE(bearer()->Reply(req_id, NewBuffer(kTestResponse)));
EXPECT_TRUE(bearer()->Reply(ind_id, NewBuffer(kConfirmation)));
}
// Test receipt of non-transactional PDUs.
TEST_F(BearerTest, RemotePDUWithoutResponse) {
int cmd_count = 0;
auto cmd_handler = [&cmd_count](auto tid, const auto& packet) {
EXPECT_EQ(Bearer::kInvalidTransactionId, tid);
EXPECT_EQ(kWriteCommand, packet.opcode());
cmd_count++;
};
bearer()->RegisterHandler(kWriteCommand, cmd_handler);
int not_count = 0;
auto not_handler = [&not_count](auto tid, const auto& packet) {
EXPECT_EQ(Bearer::kInvalidTransactionId, tid);
EXPECT_EQ(kNotification, packet.opcode());
not_count++;
};
bearer()->RegisterHandler(kNotification, not_handler);
fake_chan()->Receive(StaticByteBuffer(kTestCommand));
fake_chan()->Receive(StaticByteBuffer(kNotification));
RunLoopUntilIdle();
EXPECT_EQ(1, cmd_count);
EXPECT_EQ(1, not_count);
}
class BearerTestSecurity : public BearerTest {
protected:
void SetUp() override {
BearerTest::SetUp();
fake_chan()->SetSecurityCallback(
[this](hci_spec::ConnectionHandle handle, sm::SecurityLevel level,
sm::ResultFunction<> callback) {
security_request_count_++;
requested_security_level_ = level;
ASSERT_FALSE(security_responder_) << "Security request received while one was pending";
security_responder_ = std::move(callback);
},
dispatcher());
}
// Sets up the fake channel to send an error response to all packets it
// receives.
void SetUpErrorResponder(ErrorCode ecode, Handle handle = 1) {
fake_chan()->SetSendCallback(
[this, ecode, handle](auto packet) {
att_request_count_++;
fake_chan()->Receive(StaticByteBuffer(kErrorResponse, // opcode (Error Response)
kTestRequest, // request opcode
LowerBits(handle),
UpperBits(handle), // handle
ecode // error code
));
},
dispatcher());
}
// Sets up the fake channel to respond with the given |response| opcode to all
// requests that it receives. The PDU contains no additional payload as it is
// not needed for this test fixture.
void SetUpResponder() {
fake_chan()->SetSendCallback(
[this](auto packet) {
att_request_count_++;
fake_chan()->Receive(StaticByteBuffer(kTestResponse));
},
dispatcher());
}
// Resolves the currently pending security request.
void ResolvePendingSecurityRequest(sm::Result<> status) {
ASSERT_TRUE(security_responder_);
if (status.is_ok()) {
fake_chan()->set_security(
sm::SecurityProperties(requested_security_level_, 16, /*secure_connections=*/false));
}
// Clear the responder before invoking it.
auto f = std::move(security_responder_);
f(status);
}
void SendRequest() {
bearer()->StartTransaction(NewBuffer(kTestRequest), [this](auto result) {
if (result.is_ok()) {
request_success_count_++;
last_request_status_ = fitx::ok();
} else {
request_error_count_++;
last_request_status_ = fitx::error(result.error_value().first);
}
});
}
const Result<>& last_request_status() const { return last_request_status_; }
size_t request_success_count() const { return request_success_count_; }
size_t request_error_count() const { return request_error_count_; }
size_t att_request_count() const { return att_request_count_; }
size_t security_request_count() const { return security_request_count_; }
sm::SecurityLevel requested_security_level() const { return requested_security_level_; }
private:
Result<> last_request_status_ = fitx::ok();
size_t request_success_count_ = 0u;
size_t request_error_count_ = 0u;
size_t att_request_count_ = 0u;
sm::SecurityLevel requested_security_level_ = sm::SecurityLevel::kNoSecurity;
size_t security_request_count_ = 0u;
sm::ResultFunction<> security_responder_;
};
TEST_F(BearerTestSecurity, SecurityUpgradeAfterInsufficientAuthentication) {
// Configure the endpoint to respond with an authentication error.
SetUpErrorResponder(ErrorCode::kInsufficientAuthentication);
SendRequest();
RunLoopUntilIdle();
// At this stage the remote device should have received the request and
// responded with "Insufficient Authentication". Since the link was not
// encrypted, the Bearer should have requested a security upgrade without
// requiring MITM protection.
EXPECT_EQ(1u, att_request_count());
EXPECT_EQ(1u, security_request_count());
EXPECT_EQ(sm::SecurityLevel::kEncrypted, requested_security_level());
// The request should be still unresolved.
EXPECT_EQ(0u, request_success_count());
EXPECT_EQ(0u, request_error_count());
// Configure the endpoint to respond with success and notify the Bearer of the
// security upgrade. The Bearer should re-send the request.
SetUpResponder();
ResolvePendingSecurityRequest(fitx::ok());
RunLoopUntilIdle();
// We should have received the same request again.
EXPECT_EQ(2u, att_request_count());
EXPECT_EQ(1u, security_request_count());
EXPECT_EQ(1u, request_success_count());
EXPECT_EQ(0u, request_error_count());
}
TEST_F(BearerTestSecurity, SecurityUpgradeWithMitmAfterInsufficientAuthentication) {
// Configure the channel to be already encrypted.
fake_chan()->set_security(
sm::SecurityProperties(sm::SecurityLevel::kEncrypted, 16, /*secure_connections=*/false));
// Configure the endpoint to respond with an authentication error.
SetUpErrorResponder(ErrorCode::kInsufficientAuthentication);
SendRequest();
RunLoopUntilIdle();
// At this stage the remote device should have received the request and
// responded with "Insufficient Authentication". Since the link was already
// encrypted, the Bearer should have requested a security upgrade with MITM
// protection.
EXPECT_EQ(1u, att_request_count());
EXPECT_EQ(1u, security_request_count());
EXPECT_EQ(sm::SecurityLevel::kAuthenticated, requested_security_level());
// The request should be still unresolved.
EXPECT_EQ(0u, request_success_count());
EXPECT_EQ(0u, request_error_count());
// Configure the endpoint to respond with success and notify the Bearer of the
// security upgrade. The Bearer should re-send the request.
SetUpResponder();
ResolvePendingSecurityRequest(fitx::ok());
RunLoopUntilIdle();
// We should have received the same request again.
EXPECT_EQ(2u, att_request_count());
EXPECT_EQ(1u, security_request_count());
EXPECT_EQ(1u, request_success_count());
EXPECT_EQ(0u, request_error_count());
EXPECT_EQ(fitx::ok(), last_request_status());
}
TEST_F(BearerTestSecurity, SecurityUpgradeFailsAfterAuthError) {
// Configure the endpoint to respond with an authentication error.
SetUpErrorResponder(ErrorCode::kInsufficientAuthentication);
SendRequest();
RunLoopUntilIdle();
// At this stage the remote device should have received the request and
// responded with "Insufficient Authentication". Since the link was not
// encrypted, the Bearer should have requested a security upgrade without
// requiring MITM protection.
EXPECT_EQ(1u, att_request_count());
EXPECT_EQ(1u, security_request_count());
EXPECT_EQ(sm::SecurityLevel::kEncrypted, requested_security_level());
// The request should be still unresolved.
EXPECT_EQ(0u, request_success_count());
EXPECT_EQ(0u, request_error_count());
// Configure the endpoint to respond with success and notify the Bearer of the
// security upgrade. The Bearer should re-send the request.
SetUpResponder();
ResolvePendingSecurityRequest(ToResult(HostError::kFailed));
RunLoopUntilIdle();
// The request should not have been retried and failed with the original
// error.
EXPECT_EQ(1u, att_request_count());
EXPECT_EQ(1u, security_request_count());
EXPECT_EQ(0u, request_success_count());
EXPECT_EQ(1u, request_error_count());
EXPECT_EQ(ToResult(ErrorCode::kInsufficientAuthentication), last_request_status());
}
TEST_F(BearerTestSecurity, NoSecurityUpgradeIfAlreadyRetried) {
// Configure the endpoint to respond with an authentication error.
SetUpErrorResponder(ErrorCode::kInsufficientAuthentication);
SendRequest();
RunLoopUntilIdle();
// At this stage the remote device should have received the request and
// responded with "Insufficient Authentication". Since the link was not
// encrypted, the Bearer should have requested a security upgrade without
// requiring MITM protection.
EXPECT_EQ(1u, att_request_count());
EXPECT_EQ(1u, security_request_count());
EXPECT_EQ(sm::SecurityLevel::kEncrypted, requested_security_level());
// The request should be still unresolved.
EXPECT_EQ(0u, request_success_count());
EXPECT_EQ(0u, request_error_count());
// Resolve the pending security request with success while the channel is
// configured to respond with "Insufficient Authentication". The Bearer should
// re-send the request.
ResolvePendingSecurityRequest(fitx::ok());
RunLoopUntilIdle();
// The request should have been retried once. The "Insufficient
// Authentication" error received while the link is encrypted should result in
// a second security request at the next security level.
EXPECT_EQ(2u, att_request_count());
EXPECT_EQ(2u, security_request_count());
EXPECT_EQ(0u, request_success_count());
EXPECT_EQ(0u, request_error_count());
// Resolve the pending security request with success while the channel is
// configured to respond with "Insufficient Authentication. The Bearer should
// retry the request one last time.
ResolvePendingSecurityRequest(fitx::ok());
RunLoopUntilIdle();
// The request should have failed without retrying the request a third time as
// the highest security level has been reached.
EXPECT_EQ(3u, att_request_count());
EXPECT_EQ(2u, security_request_count());
EXPECT_EQ(0u, request_success_count());
EXPECT_EQ(1u, request_error_count());
EXPECT_EQ(ToResult(ErrorCode::kInsufficientAuthentication), last_request_status());
}
TEST_F(BearerTestSecurity, NoSecurityUpgradeIfChannelAlreadyEncrypted) {
// Configure the channel to be already encrypted.
fake_chan()->set_security(
sm::SecurityProperties(sm::SecurityLevel::kEncrypted, 16, /*secure_connections=*/false));
// Configure the endpoint to respond with an encryption error.
SetUpErrorResponder(ErrorCode::kInsufficientEncryption);
SendRequest();
RunLoopUntilIdle();
// No security upgrade should have been requested since the channel was
// sufficiently encrypted.
EXPECT_EQ(1u, att_request_count());
EXPECT_EQ(0u, security_request_count());
EXPECT_EQ(0u, request_success_count());
EXPECT_EQ(1u, request_error_count());
EXPECT_EQ(ToResult(ErrorCode::kInsufficientEncryption), last_request_status());
}
TEST_F(BearerTestSecurity, NoSecurityUpgradeIfChannelAlreadyEncryptedWithMitm) {
// Configure the channel to be already encrypted with MITM protection
fake_chan()->set_security(
sm::SecurityProperties(sm::SecurityLevel::kAuthenticated, 16, /*secure_connections=*/false));
// Configure the endpoint to respond with an authentication error.
SetUpErrorResponder(ErrorCode::kInsufficientAuthentication);
SendRequest();
RunLoopUntilIdle();
// No security upgrade should have been requested since the channel was
// sufficiently encrypted.
EXPECT_EQ(1u, att_request_count());
EXPECT_EQ(0u, security_request_count());
EXPECT_EQ(0u, request_success_count());
EXPECT_EQ(1u, request_error_count());
EXPECT_EQ(ToResult(ErrorCode::kInsufficientAuthentication), last_request_status());
}
} // namespace
} // namespace bt::att