blob: 8c388b6379e5f6d1115ffd3afa16725687ccb6ff [file]
// Copyright 2026 The Pigweed Authors
//
// 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
//
// https://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 "pw_bluetooth_proxy/hci/sniff_offload_manager.h"
#include <cstddef>
#include <cstdint>
#include "lib/stdcompat/utility.h"
#include "pw_allocator/synchronized_allocator.h"
#include "pw_allocator/testing.h"
#include "pw_assert/check.h"
#include "pw_async2/dispatcher_for_test.h"
#include "pw_async2/simulated_time_provider.h"
#include "pw_bluetooth/hci_android.emb.h"
#include "pw_bluetooth/hci_commands.emb.h"
#include "pw_bluetooth/hci_common.emb.h"
#include "pw_bluetooth/hci_data.emb.h"
#include "pw_bluetooth/hci_events.emb.h"
#include "pw_log/log.h"
#include "pw_status/status.h"
#include "pw_sync/mutex.h"
#include "pw_thread/sleep.h"
#include "pw_thread/test_thread_context.h"
#include "pw_thread/thread.h"
#include "pw_unit_test/framework.h"
#include "pw_unit_test/status_macros.h"
namespace pw::bluetooth::proxy::hci {
namespace {
using HandlerAction = SniffOffloadManager::HandlerAction;
constexpr size_t kAllocatorSize = 1024;
constexpr size_t kInternalAllocatorSize = 512;
constexpr auto kDefaultErrorAction = SniffOffloadManager::ErrorAction::kIgnore;
constexpr auto kDefaultLinkInactivityTimeout = std::chrono::milliseconds(1000);
constexpr auto kTick = Clock::duration(1);
constexpr uint16_t kConnectionHandle = 0x123;
constexpr HandlerAction kPassthroughResume = {
.intercept = HandlerAction::Interception::kPassthrough,
.resume = HandlerAction::Resumption::kResume,
};
constexpr HandlerAction kInterceptResume = {
.intercept = HandlerAction::Interception::kIntercept,
.resume = HandlerAction::Resumption::kResume,
};
class SniffOffloadManagerTest : public ::testing::Test {
protected:
using EventCode = SniffOffloadManager::EventCode;
using CommandOpcode = SniffOffloadManager::CommandOpcode;
using ErrorReason = SniffOffloadManager::ErrorReason;
struct HostCommand final {
MultiBuf::Instance buf;
SniffOffloadManager::CommandOpcode opcode;
};
struct ControllerEvent final {
MultiBuf::Instance buf;
SniffOffloadManager::EventCode event_code;
};
struct SentCommand final {
MultiBuf::Instance buf;
SniffOffloadManager::CompletionEvent completion;
std::optional<CommandOpcode> opcode();
};
struct Error final {
ErrorReason reason;
std::optional<ConnectionHandle> handle = std::nullopt;
bool operator==(const Error& other) const;
};
struct SniffOffloadParameters final {
uint16_t sniff_max_interval = 0x0010;
uint16_t sniff_min_interval = 0x0008;
uint16_t sniff_attempts = 0x0004;
uint16_t sniff_timeout = 0x0008;
uint16_t link_inactivity_timeout =
std::chrono::duration_cast<std::chrono::milliseconds>(
kDefaultLinkInactivityTimeout)
.count();
uint16_t subrating_max_latency = 0x0008;
uint16_t subrating_min_remote_timeout = 0x0000;
uint16_t subrating_min_local_timeout = 0x0000;
bool allow_exit_sniff_on_rx = true;
bool allow_exit_sniff_on_tx = true;
static SniffOffloadParameters Default() { return {}; }
};
SniffOffloadManager& MakeSniffOffloadWithHandlers(
SniffOffloadManager::SendCommandFunc&& send_command,
SniffOffloadManager::SendEventFunc&& send_event,
SniffOffloadManager::OnErrorFunc&& on_error) {
return sniff_offload_manager_.emplace(allocator_,
dispatcher_,
std::move(send_command),
std::move(send_event),
std::move(on_error),
time_provider_);
}
void DestroySniffOffload() { sniff_offload_manager_.reset(); }
SniffOffloadManager& sniff_offload_manager() {
if (!sniff_offload_manager_.has_value()) {
return MakeSniffOffloadWithHandlers(MakeDefaultSendCommand(),
MakeDefaultSendEvent(),
MakeDefaultOnError());
}
return *sniff_offload_manager_;
}
Allocator& allocator() { return allocator_; }
async2::DispatcherForTest& dispatcher() { return dispatcher_; }
template <typename Fn>
auto WithTestAllocator(Fn&& fn) {
auto borrow = allocator_.Borrow();
return fn(static_cast<decltype(test_allocator_)&>(*borrow));
}
DynamicDeque<MultiBuf::Instance>& packets_to_host() {
return packets_to_host_;
}
DynamicDeque<SentCommand>& packets_to_controller() {
return packets_to_controller_;
}
DynamicDeque<Error>& errors() { return errors_; }
HandlerAction SimulateCommand(MultiBuf::Instance&& buf,
CommandOpcode opcode) {
MultiBuf::Instance moved_buf = std::move(buf);
auto action = sniff_offload_manager().ProcessCommand(moved_buf, opcode);
SyncWithDispatcher();
return action;
}
HandlerAction SimulateEvent(MultiBuf::Instance&& buf, EventCode event_code) {
MultiBuf::Instance moved_buf = std::move(buf);
auto action = sniff_offload_manager().ProcessEvent(moved_buf, event_code);
SyncWithDispatcher();
return action;
}
Status SimulateCommandStatus(MultiBuf::Instance&& buf) {
MultiBuf::Instance moved_buf = std::move(buf);
auto status = sniff_offload_manager().ProcessCommandStatus(moved_buf);
SyncWithDispatcher();
return status;
}
HandlerAction Simulate(HostCommand&& command) {
return SimulateCommand(std::move(command.buf), command.opcode);
}
HandlerAction Simulate(ControllerEvent&& event) {
return SimulateEvent(std::move(event.buf), event.event_code);
}
void SimulateAclActivity(uint16_t handle,
SniffOffloadManager::Direction direction,
bool valid = true);
void AdvanceTime(Clock::duration duration) {
time_provider_.AdvanceTime(duration);
SyncWithDispatcher();
}
SniffOffloadManager::SendCommandFunc MakeDefaultSendCommand() {
return [this](MultiBuf::Instance&& buf,
SniffOffloadManager::CompletionEvent completion) -> Status {
return packets_to_controller_.try_push_back({std::move(buf), completion})
? OkStatus()
: Status::ResourceExhausted();
};
}
SniffOffloadManager::SendEventFunc MakeDefaultSendEvent() {
return [this](MultiBuf::Instance&& buf) -> Status {
return packets_to_host_.try_push_back(std::move(buf))
? OkStatus()
: Status::ResourceExhausted();
};
}
SniffOffloadManager::OnErrorFunc MakeDefaultOnError() {
return [this](ErrorReason reason, std::optional<ConnectionHandle> handle)
-> SniffOffloadManager::ErrorAction {
errors_.push_back({reason, handle});
return kDefaultErrorAction;
};
}
ControllerEvent ConnectionComplete(
ConnectionHandle handle,
emboss::StatusCode status = emboss::StatusCode::SUCCESS,
emboss::LinkType link_type = emboss::LinkType::ACL);
ControllerEvent ConnectionComplete(
std::underlying_type_t<ConnectionHandle> handle,
std::underlying_type_t<emboss::StatusCode> status =
cpp23::to_underlying(emboss::StatusCode::SUCCESS),
std::underlying_type_t<emboss::LinkType> link_type =
cpp23::to_underlying(emboss::LinkType::ACL)) {
return ConnectionComplete(ConnectionHandle(handle),
emboss::StatusCode(status),
emboss::LinkType(link_type));
}
ControllerEvent DisconnectionComplete(
ConnectionHandle handle,
emboss::StatusCode status = emboss::StatusCode::SUCCESS,
emboss::StatusCode reason =
emboss::StatusCode::CONNECTION_TERMINATED_BY_LOCAL_HOST);
ControllerEvent DisconnectionComplete(
std::underlying_type_t<ConnectionHandle> handle,
std::underlying_type_t<emboss::StatusCode> status =
cpp23::to_underlying(emboss::StatusCode::SUCCESS),
std::underlying_type_t<emboss::StatusCode> reason = cpp23::to_underlying(
emboss::StatusCode::CONNECTION_TERMINATED_BY_LOCAL_HOST)) {
return DisconnectionComplete(ConnectionHandle(handle),
emboss::StatusCode(status),
emboss::StatusCode(reason));
}
HostCommand WriteSniffOffloadEnable(bool enable,
bool suppress_mode_change_event = false,
bool suppress_sniff_subrating = false);
HostCommand WriteSniffOffloadParameters(
ConnectionHandle handle,
SniffOffloadParameters&& parameters = SniffOffloadParameters::Default());
HostCommand WriteSniffOffloadParameters(
std::underlying_type_t<ConnectionHandle> handle,
SniffOffloadParameters&& parameters = SniffOffloadParameters::Default()) {
return WriteSniffOffloadParameters(ConnectionHandle(handle),
std::move(parameters));
}
ControllerEvent ModeChangeEvent(
uint16_t connection_handle,
emboss::AclConnectionMode mode = emboss::AclConnectionMode::SNIFF);
ControllerEvent SniffSubratingEvent(uint16_t connection_handle);
MultiBuf::Instance MakeCommandStatus(CommandOpcode opcode,
emboss::StatusCode status);
MultiBuf::Instance MakeZeroBuffer(size_t size = 0);
ControllerEvent MakeLeGetVendorCapabilitiesCommandCompleteEvent(
size_t size,
emboss::StatusCode status = emboss::StatusCode::SUCCESS,
uint8_t version_major = 0,
uint8_t version_minor = 0);
bool CheckSniffSubratingSent(
std::optional<uint16_t> connection_handle = std::nullopt);
bool CheckSniffModeSent(
std::optional<uint16_t> connection_handle = std::nullopt);
bool CheckSniffExitSent(
std::optional<uint16_t> connection_handle = std::nullopt);
bool CheckCommandCompleteEventSent(
std::optional<CommandOpcode> opcode = std::nullopt,
std::optional<emboss::StatusCode> status = std::nullopt);
bool CheckCommandStatusEventSent(
std::optional<CommandOpcode> opcode = std::nullopt,
std::optional<emboss::StatusCode> status = std::nullopt);
bool CheckLeGetVendorCapabilitiesCommandCompleteEvent(
const MultiBuf& event,
std::optional<emboss::StatusCode> expected_status = std::nullopt,
std::optional<vendor::android_hci::Capability>
expected_sniff_offload_support = std::nullopt,
std::optional<uint8_t> expected_version_major = std::nullopt,
std::optional<uint8_t> expected_version_minor = std::nullopt);
bool NoErrors() {
auto was_empty = errors_.empty();
errors_.clear();
return was_empty;
}
std::optional<Error> PopLastError() {
if (errors_.empty()) {
return std::nullopt;
}
auto last = std::move(errors_.back());
errors_.pop_back();
return last;
}
void SyncWithDispatcher() { dispatcher().RunUntilStalled(); }
private:
void TearDown() override { DestroySniffOffload(); }
Allocator& internal_allocator() { return internal_allocator_; }
allocator::test::AllocatorForTest<kAllocatorSize> test_allocator_;
allocator::SynchronizedAllocator<sync::Mutex> allocator_{test_allocator_};
allocator::test::AllocatorForTest<kInternalAllocatorSize> internal_allocator_;
allocator::SynchronizedAllocator<sync::Mutex> sync_internal_allocator_{
internal_allocator_};
async2::DispatcherForTest dispatcher_;
async2::SimulatedTimeProvider<Clock> time_provider_;
std::optional<SniffOffloadManager> sniff_offload_manager_;
pw::DynamicDeque<MultiBuf::Instance> packets_to_host_{
sync_internal_allocator_};
pw::DynamicDeque<SentCommand> packets_to_controller_{
sync_internal_allocator_};
pw::DynamicDeque<Error> errors_{sync_internal_allocator_};
};
TEST_F(SniffOffloadManagerTest, CreationAndDestruction) {
DestroySniffOffload();
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, CreationAndDestructionActiveConnection) {
EXPECT_EQ(Simulate(ConnectionComplete(0x0123)), kPassthroughResume);
EXPECT_EQ(Simulate(ConnectionComplete(0x0456)), kPassthroughResume);
DestroySniffOffload();
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, CreationAndDestructionAllConnectionsClosed) {
EXPECT_EQ(Simulate(ConnectionComplete(0x0123)), kPassthroughResume);
EXPECT_EQ(Simulate(ConnectionComplete(0x0456)), kPassthroughResume);
EXPECT_EQ(Simulate(DisconnectionComplete(0x0123)), kPassthroughResume);
EXPECT_EQ(Simulate(DisconnectionComplete(0x0456)), kPassthroughResume);
DestroySniffOffload();
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, EnableSniffBeforeConnection) {
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(DisconnectionComplete(0x123)), kPassthroughResume);
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, EnableSniffAfterConnection) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(DisconnectionComplete(0x123)), kPassthroughResume);
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, AutosniffOnTimeout) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x123)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(CheckSniffSubratingSent(0x123));
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout + kTick);
EXPECT_TRUE(CheckSniffModeSent());
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, AutoSniffOnTimeoutExitOnRx) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x123)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(CheckSniffSubratingSent(0x123));
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout + kTick);
EXPECT_TRUE(CheckSniffModeSent());
EXPECT_TRUE(NoErrors());
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kRx);
EXPECT_TRUE(CheckSniffExitSent(0x123));
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, AutoSniffOnTimeoutExitOnTx) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x123)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(CheckSniffSubratingSent(0x123));
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout + kTick);
EXPECT_TRUE(CheckSniffModeSent());
EXPECT_TRUE(NoErrors());
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kTx);
EXPECT_TRUE(CheckSniffExitSent(0x123));
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, AutoSniffOnTimeoutNoExitOnRx) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(
0x123, {.allow_exit_sniff_on_rx = false})),
kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(CheckSniffSubratingSent(0x123));
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout + kTick);
EXPECT_TRUE(CheckSniffModeSent());
EXPECT_TRUE(NoErrors());
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kRx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, AutoSniffOnTimeoutNoExitOnTx) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(
0x123, {.allow_exit_sniff_on_tx = false})),
kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(CheckSniffSubratingSent(0x123));
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout + kTick);
EXPECT_TRUE(CheckSniffModeSent());
EXPECT_TRUE(NoErrors());
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kTx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, AutoSniffDelaysOnAclActivityTx) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x123)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(CheckSniffSubratingSent(0x123));
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kTx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kTx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kTx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kTx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kTx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
EXPECT_TRUE(CheckSniffModeSent());
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, AutoSniffDelaysOnAclActivityRx) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x123)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(CheckSniffSubratingSent(0x123));
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kRx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kRx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kRx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kRx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kRx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
EXPECT_TRUE(CheckSniffModeSent());
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, AutoSniffDelaysOnAclActivityMixed) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x123)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(CheckSniffSubratingSent(0x123));
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kRx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kTx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kTx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kRx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kTx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
EXPECT_TRUE(CheckSniffModeSent(0x123));
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, AutosniffResetsTimerOnModeChangeActive) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x123)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(CheckSniffSubratingSent(0x123));
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout * 2 / 3 + kTick);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
EXPECT_EQ(Simulate(ModeChangeEvent(0x123, emboss::AclConnectionMode::ACTIVE)),
kPassthroughResume);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout * 2 / 3 + kTick);
EXPECT_FALSE(CheckSniffModeSent());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout * 2 / 3 + kTick);
EXPECT_TRUE(CheckSniffModeSent(0x123));
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, AutosniffDoesNotSendSniffModeIfAlreadySniff) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x123)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(CheckSniffSubratingSent(0x123));
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout * 2 / 3 + kTick);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
EXPECT_EQ(Simulate(ModeChangeEvent(0x123, emboss::AclConnectionMode::SNIFF)),
kPassthroughResume);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout * 2 / 3 + kTick);
EXPECT_FALSE(CheckSniffModeSent());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout * 2 / 3 + kTick);
EXPECT_FALSE(CheckSniffModeSent());
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, AutosniffTreatsHoldModeAsSniff) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x123)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(CheckSniffSubratingSent(0x123));
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout * 2 / 3 + kTick);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
EXPECT_EQ(Simulate(ModeChangeEvent(0x123, emboss::AclConnectionMode::HOLD)),
kPassthroughResume);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout * 2 / 3 + kTick);
EXPECT_FALSE(CheckSniffModeSent());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout * 2 / 3 + kTick);
EXPECT_FALSE(CheckSniffModeSent());
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, AutosniffDisable) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x123)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(CheckSniffSubratingSent(0x123));
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(false)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2 + kTick);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest,
AclActivityInSniffModeWhenDisabledDoesNotCrash) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x123)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(CheckSniffSubratingSent(0x123));
EXPECT_TRUE(NoErrors());
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(false)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
EXPECT_EQ(Simulate(ModeChangeEvent(0x123, emboss::AclConnectionMode::SNIFF)),
kPassthroughResume);
EXPECT_TRUE(NoErrors());
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kRx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kTx);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, SuppressModeChange) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true, true, true)),
kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(ModeChangeEvent(0x123)), kInterceptResume);
EXPECT_TRUE(NoErrors());
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true, true, false)),
kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(ModeChangeEvent(0x123)), kInterceptResume);
EXPECT_TRUE(NoErrors());
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true, false, true)),
kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(ModeChangeEvent(0x123)), kPassthroughResume);
EXPECT_TRUE(NoErrors());
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true, false, false)),
kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(ModeChangeEvent(0x123)), kPassthroughResume);
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, SuppressSniffSubrating) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true, true, true)),
kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(SniffSubratingEvent(0x123)), kInterceptResume);
EXPECT_TRUE(NoErrors());
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true, true, false)),
kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(SniffSubratingEvent(0x123)), kPassthroughResume);
EXPECT_TRUE(NoErrors());
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true, false, true)),
kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(SniffSubratingEvent(0x123)), kInterceptResume);
EXPECT_TRUE(NoErrors());
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true, false, false)),
kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(SniffSubratingEvent(0x123)), kPassthroughResume);
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, PushActiveFromPending) {
EXPECT_EQ(Simulate(ConnectionComplete(kConnectionHandle)),
kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE));
// Send parameters with sniff_max_interval = 0
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(kConnectionHandle,
{.sniff_max_interval = 0})),
kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS));
// Should send Exit Sniff Mode
EXPECT_TRUE(CheckSniffExitSent(kConnectionHandle));
EXPECT_TRUE(NoErrors());
// Timer should be stopped, so advancing time should not send anything.
AdvanceTime(kDefaultLinkInactivityTimeout + kTick);
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, PushActiveFromControlStarted) {
EXPECT_EQ(Simulate(ConnectionComplete(kConnectionHandle)),
kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE));
// Send normal parameters
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(kConnectionHandle)),
kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS));
EXPECT_TRUE(CheckSniffSubratingSent(kConnectionHandle));
// Send parameters with sniff_max_interval = 0
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(kConnectionHandle,
{.sniff_max_interval = 0})),
kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS));
// Should send Exit Sniff Mode
EXPECT_TRUE(CheckSniffExitSent(kConnectionHandle));
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, TransitionFromPushActiveToControlStarted) {
EXPECT_EQ(Simulate(ConnectionComplete(kConnectionHandle)),
kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE));
// Send parameters with sniff_max_interval = 0
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(kConnectionHandle,
{.sniff_max_interval = 0})),
kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS));
EXPECT_TRUE(CheckSniffExitSent(kConnectionHandle));
// Send normal parameters
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(kConnectionHandle)),
kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS));
// Should send Sniff Subrating
EXPECT_TRUE(CheckSniffSubratingSent(kConnectionHandle));
EXPECT_TRUE(NoErrors());
// Timer should be started, so advancing time should send Sniff Mode.
AdvanceTime(kDefaultLinkInactivityTimeout + kTick);
EXPECT_TRUE(CheckSniffModeSent(kConnectionHandle));
EXPECT_TRUE(NoErrors());
}
TEST_F(SniffOffloadManagerTest, ErrorCannotAllocateConnection) {
WithTestAllocator([](auto& allocator) { allocator.Exhaust(); });
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(
PopLastError(),
Error({ErrorReason::kCannotAllocateConnection, ConnectionHandle{0x123}}));
}
TEST_F(SniffOffloadManagerTest, ErrorCannotAllocateBuffer) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
WithTestAllocator([](auto& allocator) { allocator.Exhaust(); });
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_EQ(PopLastError(),
Error({ErrorReason::kCannotAllocateBuffer, std::nullopt}));
EXPECT_FALSE(CheckCommandCompleteEventSent());
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x123)), kInterceptResume);
EXPECT_EQ(PopLastError(),
Error({ErrorReason::kCannotAllocateBuffer, std::nullopt}));
EXPECT_FALSE(CheckCommandCompleteEventSent());
EXPECT_EQ(
PopLastError(),
Error({ErrorReason::kCannotAllocateBuffer, ConnectionHandle{0x123}}));
EXPECT_FALSE(CheckSniffSubratingSent());
AdvanceTime(kDefaultLinkInactivityTimeout + kTick);
EXPECT_EQ(
PopLastError(),
Error({ErrorReason::kCannotAllocateBuffer, ConnectionHandle{0x123}}));
EXPECT_FALSE(CheckSniffModeSent());
EXPECT_TRUE(NoErrors());
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(packets_to_host().empty());
}
TEST_F(SniffOffloadManagerTest, ErrorConnectionAlreadyExists) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_TRUE(NoErrors());
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(
PopLastError(),
Error({ErrorReason::kConnectionAlreadyExists, ConnectionHandle{0x123}}));
EXPECT_TRUE(NoErrors());
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(packets_to_host().empty());
}
TEST_F(SniffOffloadManagerTest, ErrorConnectionNotFound) {
SimulateAclActivity(0x123, SniffOffloadManager::Direction::kRx);
EXPECT_EQ(PopLastError(),
Error({ErrorReason::kConnectionNotFound, ConnectionHandle{0x123}}));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x456)), kInterceptResume);
EXPECT_EQ(PopLastError(),
Error({ErrorReason::kConnectionNotFound, ConnectionHandle{0x456}}));
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::UNKNOWN_CONNECTION_ID));
EXPECT_EQ(Simulate(DisconnectionComplete(0x555)), kPassthroughResume);
EXPECT_EQ(PopLastError(),
Error({ErrorReason::kConnectionNotFound, ConnectionHandle{0x555}}));
EXPECT_EQ(Simulate(ModeChangeEvent(0x333)), kPassthroughResume);
EXPECT_EQ(PopLastError(),
Error({ErrorReason::kConnectionNotFound, ConnectionHandle{0x333}}));
EXPECT_TRUE(NoErrors());
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(packets_to_host().empty());
}
TEST_F(SniffOffloadManagerTest, ErrorInvalidPacket) {
constexpr size_t kWriteSniffOffloadEnableSize = vendor::android_hci::
WriteSniffOffloadEnableCommand::IntrinsicSizeInBytes();
constexpr size_t kWriteSniffOffloadParametersSize = vendor::android_hci::
WriteSniffOffloadParametersCommand::IntrinsicSizeInBytes();
constexpr size_t kSniffModeSize =
emboss::SniffModeCommand::IntrinsicSizeInBytes();
constexpr size_t kExitSniffModeSize =
emboss::ExitSniffModeCommand::IntrinsicSizeInBytes();
EXPECT_EQ(SimulateCommand(MakeZeroBuffer(),
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE),
kInterceptResume);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::INVALID_HCI_COMMAND_PARAMETERS));
EXPECT_EQ(SimulateCommand(MakeZeroBuffer(kWriteSniffOffloadEnableSize),
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE),
kInterceptResume);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::INVALID_HCI_COMMAND_PARAMETERS));
EXPECT_EQ(
SimulateCommand(MakeZeroBuffer(),
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS),
kInterceptResume);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::INVALID_HCI_COMMAND_PARAMETERS));
EXPECT_EQ(
SimulateCommand(MakeZeroBuffer(kWriteSniffOffloadParametersSize),
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS),
kInterceptResume);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::INVALID_HCI_COMMAND_PARAMETERS));
EXPECT_EQ(SimulateCommand(MakeZeroBuffer(), CommandOpcode::SNIFF_MODE),
kPassthroughResume);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_EQ(SimulateCommand(MakeZeroBuffer(kSniffModeSize),
CommandOpcode::SNIFF_MODE),
kPassthroughResume);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_EQ(SimulateCommand(MakeZeroBuffer(), CommandOpcode::EXIT_SNIFF_MODE),
kPassthroughResume);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_EQ(SimulateCommand(MakeZeroBuffer(kExitSniffModeSize),
CommandOpcode::EXIT_SNIFF_MODE),
kPassthroughResume);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_TRUE(NoErrors());
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(packets_to_host().empty());
constexpr size_t kModeChangeEventSize =
emboss::ModeChangeEvent::IntrinsicSizeInBytes();
constexpr size_t kConnectionCompleteEventSize =
emboss::ConnectionCompleteEvent::IntrinsicSizeInBytes();
constexpr size_t kDisconnectionCompleteEventSize =
emboss::DisconnectionCompleteEvent::IntrinsicSizeInBytes();
constexpr size_t kSniffSubratingEventSize =
emboss::EventHeader::IntrinsicSizeInBytes();
constexpr size_t kCommandCompleteEventSize =
emboss::CommandCompleteEvent::IntrinsicSizeInBytes();
EXPECT_EQ(SimulateEvent(MakeZeroBuffer(), EventCode::MODE_CHANGE),
kPassthroughResume);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_EQ(SimulateEvent(MakeZeroBuffer(kModeChangeEventSize),
EventCode::MODE_CHANGE),
kPassthroughResume);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_EQ(SimulateEvent(MakeZeroBuffer(), EventCode::CONNECTION_COMPLETE),
kPassthroughResume);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_EQ(SimulateEvent(MakeZeroBuffer(kConnectionCompleteEventSize),
EventCode::CONNECTION_COMPLETE),
kPassthroughResume);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_EQ(SimulateEvent(MakeZeroBuffer(), EventCode::DISCONNECTION_COMPLETE),
kPassthroughResume);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_EQ(SimulateEvent(MakeZeroBuffer(kDisconnectionCompleteEventSize),
EventCode::DISCONNECTION_COMPLETE),
kPassthroughResume);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_EQ(SimulateEvent(MakeZeroBuffer(), EventCode::SNIFF_SUBRATING),
kPassthroughResume);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_EQ(SimulateEvent(MakeZeroBuffer(kSniffSubratingEventSize),
EventCode::SNIFF_SUBRATING),
kPassthroughResume);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_EQ(SimulateEvent(MakeZeroBuffer(), EventCode::COMMAND_COMPLETE),
HandlerAction{});
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_EQ(Simulate(MakeLeGetVendorCapabilitiesCommandCompleteEvent(
kCommandCompleteEventSize)),
HandlerAction{});
EXPECT_TRUE(NoErrors());
EXPECT_TRUE(NoErrors());
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(packets_to_host().empty());
SimulateAclActivity(0, SniffOffloadManager::Direction::kRx, false);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
SimulateAclActivity(0, SniffOffloadManager::Direction::kTx, false);
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_TRUE(NoErrors());
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(packets_to_host().empty());
}
TEST_F(SniffOffloadManagerTest, ErrorSendCommandFailed) {
MakeSniffOffloadWithHandlers([](auto&&, auto) { return Status::Internal(); },
MakeDefaultSendEvent(),
MakeDefaultOnError());
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x123)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_EQ(PopLastError(),
Error({ErrorReason::kSendCommandFailed, std::nullopt}));
}
TEST_F(SniffOffloadManagerTest, ErrorSendEventFailed) {
MakeSniffOffloadWithHandlers(
MakeDefaultSendCommand(),
[](auto&&) { return Status::Internal(); },
MakeDefaultOnError());
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_FALSE(CheckCommandCompleteEventSent());
EXPECT_EQ(PopLastError(),
Error({ErrorReason::kSendEventFailed, std::nullopt}));
}
TEST_F(SniffOffloadManagerTest, ProcessCommandStatus) {
EXPECT_EQ(SimulateCommandStatus(MakeZeroBuffer()), Status::InvalidArgument());
EXPECT_EQ(PopLastError(), Error({ErrorReason::kInvalidPacket, std::nullopt}));
EXPECT_EQ(SimulateCommandStatus(MakeCommandStatus(
CommandOpcode::INQUIRY, emboss::StatusCode::SUCCESS)),
Status::FailedPrecondition());
EXPECT_TRUE(NoErrors());
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE,
emboss::StatusCode::SUCCESS));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x123)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS,
emboss::StatusCode::SUCCESS));
EXPECT_TRUE(CheckSniffSubratingSent(0x123));
EXPECT_TRUE(NoErrors());
EXPECT_EQ(SimulateCommandStatus(MakeCommandStatus(
CommandOpcode::SNIFF_SUBRATING, emboss::StatusCode::SUCCESS)),
OkStatus());
AdvanceTime(kDefaultLinkInactivityTimeout + kTick);
EXPECT_TRUE(CheckSniffModeSent());
EXPECT_TRUE(NoErrors());
EXPECT_EQ(SimulateCommandStatus(
MakeCommandStatus(CommandOpcode::SNIFF_MODE,
emboss::StatusCode::UNKNOWN_CONNECTION_ID)),
OkStatus());
EXPECT_EQ(PopLastError(), Error({ErrorReason::kCommandStatusFailed}));
EXPECT_TRUE(NoErrors());
EXPECT_TRUE(packets_to_controller().empty());
EXPECT_TRUE(packets_to_host().empty());
}
TEST_F(SniffOffloadManagerTest, ProcessLeGetVendorCapabilities) {
static constexpr size_t v0_55_size =
vendor::android_hci::LEGetVendorCapabilitiesCommandCompleteEventView::
version_0_55_size()
.Read();
static constexpr size_t v0_95_size =
vendor::android_hci::LEGetVendorCapabilitiesCommandCompleteEventView::
version_0_95_size()
.Read();
static constexpr size_t v1_05_size =
vendor::android_hci::LEGetVendorCapabilitiesCommandCompleteEventView::
version_1_05_size()
.Read();
// Test with version 0.55 size
{
auto event = MakeLeGetVendorCapabilitiesCommandCompleteEvent(v0_55_size);
EXPECT_EQ(sniff_offload_manager().ProcessEvent(event.buf, event.event_code),
kPassthroughResume);
EXPECT_TRUE(NoErrors());
EXPECT_TRUE(packets_to_host().empty());
EXPECT_TRUE(CheckLeGetVendorCapabilitiesCommandCompleteEvent(
event.buf,
emboss::StatusCode::SUCCESS,
vendor::android_hci::Capability::CAPABLE,
1,
5));
}
// Test with version 0.95 size (0.95 overridden to 1.05)
{
auto event = MakeLeGetVendorCapabilitiesCommandCompleteEvent(
v0_95_size, emboss::StatusCode::SUCCESS, 0, 95);
EXPECT_EQ(sniff_offload_manager().ProcessEvent(event.buf, event.event_code),
kPassthroughResume);
EXPECT_TRUE(NoErrors());
EXPECT_TRUE(packets_to_host().empty());
EXPECT_TRUE(CheckLeGetVendorCapabilitiesCommandCompleteEvent(
event.buf,
emboss::StatusCode::SUCCESS,
vendor::android_hci::Capability::CAPABLE,
1,
5));
}
// Test with version 1.05 size but version 1.04 reported (overridden to 1.05)
{
auto event = MakeLeGetVendorCapabilitiesCommandCompleteEvent(
v1_05_size, emboss::StatusCode::SUCCESS, 1, 4);
EXPECT_EQ(sniff_offload_manager().ProcessEvent(event.buf, event.event_code),
kPassthroughResume);
EXPECT_TRUE(NoErrors());
EXPECT_TRUE(packets_to_host().empty());
EXPECT_TRUE(CheckLeGetVendorCapabilitiesCommandCompleteEvent(
event.buf,
emboss::StatusCode::SUCCESS,
vendor::android_hci::Capability::CAPABLE,
1,
5));
}
// Test with version 1.05 size and version 1.05 reported (not overridden)
{
auto event = MakeLeGetVendorCapabilitiesCommandCompleteEvent(
v1_05_size, emboss::StatusCode::SUCCESS, 1, 5);
EXPECT_EQ(sniff_offload_manager().ProcessEvent(event.buf, event.event_code),
kPassthroughResume);
EXPECT_TRUE(NoErrors());
EXPECT_TRUE(packets_to_host().empty());
EXPECT_TRUE(CheckLeGetVendorCapabilitiesCommandCompleteEvent(
event.buf,
emboss::StatusCode::SUCCESS,
vendor::android_hci::Capability::CAPABLE,
1,
5));
}
// Test with version 1.05 size and version 1.06 reported (not overridden)
{
auto event = MakeLeGetVendorCapabilitiesCommandCompleteEvent(
v1_05_size, emboss::StatusCode::SUCCESS, 1, 6);
EXPECT_EQ(sniff_offload_manager().ProcessEvent(event.buf, event.event_code),
kPassthroughResume);
EXPECT_TRUE(NoErrors());
EXPECT_TRUE(packets_to_host().empty());
EXPECT_TRUE(CheckLeGetVendorCapabilitiesCommandCompleteEvent(
event.buf,
emboss::StatusCode::SUCCESS,
vendor::android_hci::Capability::CAPABLE,
1,
6));
}
// Test with version 1.05 size and version 2.0 reported (not overridden)
{
auto event = MakeLeGetVendorCapabilitiesCommandCompleteEvent(
v1_05_size, emboss::StatusCode::SUCCESS, 2, 0);
EXPECT_EQ(sniff_offload_manager().ProcessEvent(event.buf, event.event_code),
kPassthroughResume);
EXPECT_TRUE(NoErrors());
EXPECT_TRUE(packets_to_host().empty());
EXPECT_TRUE(CheckLeGetVendorCapabilitiesCommandCompleteEvent(
event.buf,
emboss::StatusCode::SUCCESS,
vendor::android_hci::Capability::CAPABLE,
2,
0));
}
// Test with UNKNOWN_COMMAND status
{
constexpr size_t short_size = 6;
auto event = MakeLeGetVendorCapabilitiesCommandCompleteEvent(
short_size, emboss::StatusCode::UNKNOWN_COMMAND);
EXPECT_EQ(sniff_offload_manager().ProcessEvent(event.buf, event.event_code),
kPassthroughResume);
EXPECT_TRUE(NoErrors());
EXPECT_TRUE(packets_to_host().empty());
EXPECT_TRUE(CheckLeGetVendorCapabilitiesCommandCompleteEvent(
event.buf,
emboss::StatusCode::SUCCESS,
vendor::android_hci::Capability::CAPABLE,
1,
5));
}
// Test with HARDWARE_FAILURE status
{
auto event = MakeLeGetVendorCapabilitiesCommandCompleteEvent(
v1_05_size, emboss::StatusCode::HARDWARE_FAILURE);
EXPECT_EQ(sniff_offload_manager().ProcessEvent(event.buf, event.event_code),
HandlerAction{});
EXPECT_TRUE(NoErrors());
EXPECT_TRUE(packets_to_host().empty());
}
}
TEST_F(SniffOffloadManagerTest, Reset) {
EXPECT_EQ(Simulate(ConnectionComplete(0x123)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x123)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS));
EXPECT_TRUE(CheckSniffSubratingSent(0x123));
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout / 2);
sniff_offload_manager().Reset();
EXPECT_EQ(Simulate(ModeChangeEvent(0x123)), kPassthroughResume);
EXPECT_EQ(PopLastError(),
Error({ErrorReason::kConnectionNotFound, ConnectionHandle{0x123}}));
// The previous connection's timer should have been canceled, so no SniffMode
// command should be sent.
AdvanceTime(kDefaultLinkInactivityTimeout + kTick);
EXPECT_FALSE(CheckSniffModeSent());
// Confirm that a new connection works after the reset.
EXPECT_EQ(Simulate(ConnectionComplete(0x456)), kPassthroughResume);
EXPECT_EQ(Simulate(WriteSniffOffloadEnable(true)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE));
EXPECT_EQ(Simulate(WriteSniffOffloadParameters(0x456)), kInterceptResume);
EXPECT_TRUE(CheckCommandCompleteEventSent(
CommandOpcode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS));
EXPECT_TRUE(CheckSniffSubratingSent(0x456));
EXPECT_TRUE(NoErrors());
AdvanceTime(kDefaultLinkInactivityTimeout + kTick);
EXPECT_TRUE(CheckSniffModeSent(0x456));
EXPECT_TRUE(NoErrors());
}
std::optional<SniffOffloadManager::CommandOpcode>
SniffOffloadManagerTest::SentCommand::opcode() {
std::array<std::byte, emboss::CommandHeader::IntrinsicSizeInBytes()> scratch;
auto span = buf->Get(scratch);
emboss::CommandHeaderView view(span.data(), span.size());
if (!view.Ok()) {
return std::nullopt;
}
return CommandOpcode{cpp23::to_underlying(view.opcode().Read())};
}
bool SniffOffloadManagerTest::Error::operator==(const Error& other) const {
return reason == other.reason && handle == other.handle;
}
SniffOffloadManagerTest::ControllerEvent
SniffOffloadManagerTest::ConnectionComplete(ConnectionHandle handle,
emboss::StatusCode status,
emboss::LinkType link_type) {
auto alloc = internal_allocator().MakeUnique<std::byte[]>(
emboss::ConnectionCompleteEvent::IntrinsicSizeInBytes());
PW_CHECK(alloc != nullptr, "Test alloc failed");
memset(alloc.get(), 0, alloc.size());
MultiBuf::Instance buf{internal_allocator_};
PW_CHECK(buf->TryReserveForPushBack());
emboss::ConnectionCompleteEventWriter writer(alloc.get(), alloc.size());
PW_CHECK(writer.IsComplete());
writer.header().event_code().Write(emboss::EventCode::CONNECTION_COMPLETE);
writer.header().parameter_total_size().Write(
emboss::ConnectionCompleteEvent::IntrinsicSizeInBytes() -
emboss::EventHeader::IntrinsicSizeInBytes());
writer.link_type().Write(link_type);
writer.connection_handle().Write(cpp23::to_underlying(handle));
writer.status().Write(status);
PW_CHECK(writer.Ok());
buf->PushBack(std::move(alloc));
EventCode event_code{
cpp23::to_underlying(emboss::EventCode::CONNECTION_COMPLETE)};
return {std::move(buf), event_code};
}
SniffOffloadManagerTest::ControllerEvent
SniffOffloadManagerTest::DisconnectionComplete(ConnectionHandle handle,
emboss::StatusCode status,
emboss::StatusCode reason) {
auto alloc = internal_allocator().MakeUnique<std::byte[]>(
emboss::DisconnectionCompleteEvent::IntrinsicSizeInBytes());
PW_CHECK(alloc != nullptr, "Test alloc failed");
memset(alloc.get(), 0, alloc.size());
MultiBuf::Instance buf{internal_allocator_};
PW_CHECK(buf->TryReserveForPushBack());
emboss::DisconnectionCompleteEventWriter writer(alloc.get(), alloc.size());
PW_CHECK(writer.IsComplete());
writer.header().event_code().Write(emboss::EventCode::DISCONNECTION_COMPLETE);
writer.header().parameter_total_size().Write(
emboss::DisconnectionCompleteEvent::IntrinsicSizeInBytes() -
emboss::EventHeader::IntrinsicSizeInBytes());
writer.connection_handle().Write(cpp23::to_underlying(handle));
writer.status().Write(status);
writer.reason().Write(reason);
PW_CHECK(writer.Ok());
buf->PushBack(std::move(alloc));
EventCode event_code{
cpp23::to_underlying(emboss::EventCode::DISCONNECTION_COMPLETE)};
return {std::move(buf), event_code};
}
SniffOffloadManagerTest::HostCommand
SniffOffloadManagerTest::WriteSniffOffloadEnable(
bool enable,
bool suppress_mode_change_event,
bool suppress_sniff_subrating) {
auto alloc = internal_allocator().MakeUnique<std::byte[]>(
vendor::android_hci::WriteSniffOffloadEnableCommand::
IntrinsicSizeInBytes());
PW_CHECK(alloc != nullptr, "Test alloc failed");
memset(alloc.get(), 0, alloc.size());
MultiBuf::Instance buf{internal_allocator_};
PW_CHECK(buf->TryReserveForPushBack());
vendor::android_hci::WriteSniffOffloadEnableCommandWriter writer(
alloc.get(), alloc.size());
PW_CHECK(writer.IsComplete());
writer.header().opcode().Write(
emboss::OpCode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE);
writer.header().parameter_total_size().Write(
vendor::android_hci::WriteSniffOffloadEnableCommand::
IntrinsicSizeInBytes() -
emboss::CommandHeader::IntrinsicSizeInBytes());
writer.enable().Write(emboss::GenericEnableParam{enable});
writer.suppress_mode_change_event().Write(
vendor::android_hci::SniffOffloadEventSuppression{
suppress_mode_change_event});
writer.suppress_sniff_subrating_event().Write(
vendor::android_hci::SniffOffloadEventSuppression{
suppress_sniff_subrating});
writer.max_latency().Write(0x0002);
writer.min_remote_timeout().Write(0);
writer.min_local_timeout().Write(0);
PW_CHECK(writer.Ok());
buf->PushBack(std::move(alloc));
SniffOffloadManager::CommandOpcode opcode{
cpp23::to_underlying(emboss::OpCode::ANDROID_WRITE_SNIFF_OFFLOAD_ENABLE)};
return {std::move(buf), opcode};
}
SniffOffloadManagerTest::HostCommand
SniffOffloadManagerTest::WriteSniffOffloadParameters(
ConnectionHandle handle, SniffOffloadParameters&& parameters) {
auto alloc = internal_allocator().MakeUnique<std::byte[]>(
vendor::android_hci::WriteSniffOffloadParametersCommand::
IntrinsicSizeInBytes());
PW_CHECK(alloc != nullptr, "Test alloc failed");
memset(alloc.get(), 0, alloc.size());
MultiBuf::Instance buf{internal_allocator_};
PW_CHECK(buf->TryReserveForPushBack());
vendor::android_hci::WriteSniffOffloadParametersCommandWriter writer(
alloc.get(), alloc.size());
PW_CHECK(writer.IsComplete());
writer.header().opcode().Write(
emboss::OpCode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS);
writer.header().parameter_total_size().Write(
vendor::android_hci::WriteSniffOffloadParametersCommand::
IntrinsicSizeInBytes() -
emboss::CommandHeader::IntrinsicSizeInBytes());
writer.connection_handle().Write(cpp23::to_underlying(handle));
writer.max_interval().Write(parameters.sniff_max_interval);
writer.min_interval().Write(parameters.sniff_min_interval);
writer.attempts().Write(parameters.sniff_attempts);
writer.sniff_timeout().Write(parameters.sniff_timeout);
writer.link_inactivity_timeout().Write(parameters.link_inactivity_timeout);
writer.max_latency().Write(parameters.subrating_max_latency);
writer.min_remote_timeout().Write(parameters.subrating_min_local_timeout);
writer.min_local_timeout().Write(parameters.subrating_min_local_timeout);
writer.allow_exit_sniff_on_rx().Write(
vendor::android_hci::SniffOffloadAllowExit{
parameters.allow_exit_sniff_on_rx});
writer.allow_exit_sniff_on_tx().Write(
vendor::android_hci::SniffOffloadAllowExit{
parameters.allow_exit_sniff_on_tx});
PW_CHECK(writer.Ok());
buf->PushBack(std::move(alloc));
SniffOffloadManager::CommandOpcode opcode{cpp23::to_underlying(
emboss::OpCode::ANDROID_WRITE_SNIFF_OFFLOAD_PARAMETERS)};
return {std::move(buf), opcode};
}
SniffOffloadManagerTest::ControllerEvent
SniffOffloadManagerTest::ModeChangeEvent(uint16_t connection_handle,
emboss::AclConnectionMode mode) {
auto alloc = internal_allocator().MakeUnique<std::byte[]>(
emboss::ModeChangeEvent::IntrinsicSizeInBytes());
PW_CHECK(alloc != nullptr, "Test alloc failed");
memset(alloc.get(), 0, alloc.size());
MultiBuf::Instance buf{internal_allocator_};
PW_CHECK(buf->TryReserveForPushBack());
emboss::ModeChangeEventWriter writer(alloc.get(), alloc.size());
PW_CHECK(writer.IsComplete());
writer.header().event_code().Write(emboss::EventCode::MODE_CHANGE);
writer.header().parameter_total_size().Write(
emboss::ModeChangeEvent::IntrinsicSizeInBytes() -
emboss::EventHeader::IntrinsicSizeInBytes());
writer.connection_handle().Write(connection_handle);
writer.current_mode().Write(mode);
PW_CHECK(writer.Ok());
buf->PushBack(std::move(alloc));
EventCode event_code{cpp23::to_underlying(emboss::EventCode::MODE_CHANGE)};
return {std::move(buf), event_code};
}
SniffOffloadManagerTest::ControllerEvent
SniffOffloadManagerTest::SniffSubratingEvent(
[[maybe_unused]] uint16_t connection_handle) {
// TODO: pwbug.dev/482481726 - Need emboss definition for this.
auto alloc = internal_allocator().MakeUnique<std::byte[]>(
emboss::EventHeader::IntrinsicSizeInBytes());
PW_CHECK(alloc != nullptr, "Test alloc failed");
memset(alloc.get(), 0, alloc.size());
MultiBuf::Instance buf{internal_allocator_};
PW_CHECK(buf->TryReserveForPushBack());
emboss::EventHeaderWriter writer(alloc.get(), alloc.size());
PW_CHECK(writer.IsComplete());
writer.event_code().Write(emboss::EventCode::SNIFF_SUBRATING);
writer.parameter_total_size().Write(0);
PW_CHECK(writer.Ok());
buf->PushBack(std::move(alloc));
EventCode event_code{
cpp23::to_underlying(emboss::EventCode::SNIFF_SUBRATING)};
return {std::move(buf), event_code};
}
MultiBuf::Instance SniffOffloadManagerTest::MakeCommandStatus(
SniffOffloadManager::CommandOpcode opcode, emboss::StatusCode status) {
auto alloc = internal_allocator().MakeUnique<std::byte[]>(
emboss::CommandStatusEvent::IntrinsicSizeInBytes());
PW_CHECK(alloc != nullptr, "Test alloc failed");
memset(alloc.get(), 0, alloc.size());
MultiBuf::Instance buf{internal_allocator_};
PW_CHECK(buf->TryReserveForPushBack());
emboss::CommandStatusEventWriter writer(alloc.get(), alloc.size());
PW_CHECK(writer.IsComplete());
writer.header().event_code().Write(emboss::EventCode::COMMAND_STATUS);
writer.header().parameter_total_size().Write(
emboss::CommandStatusEvent::IntrinsicSizeInBytes() -
emboss::EventHeader::IntrinsicSizeInBytes());
writer.status().Write(status);
writer.command_opcode_enum().Write(opcode);
PW_CHECK(writer.Ok());
buf->PushBack(std::move(alloc));
return buf;
}
void SniffOffloadManagerTest::SimulateAclActivity(
uint16_t handle, SniffOffloadManager::Direction direction, bool valid) {
if (!valid) {
sniff_offload_manager().ProcessAclPacket(MakeZeroBuffer(), direction);
SyncWithDispatcher();
return;
}
auto alloc = internal_allocator().MakeUnique<std::byte[]>(
emboss::AclDataFrameHeader::IntrinsicSizeInBytes());
PW_CHECK(alloc != nullptr, "Test alloc failed");
memset(alloc.get(), 0, alloc.size());
MultiBuf::Instance buf{internal_allocator_};
PW_CHECK(buf->TryReserveForPushBack());
emboss::AclDataFrameHeaderWriter writer(alloc.get(), alloc.size());
PW_CHECK(writer.IsComplete());
writer.handle().Write(handle);
writer.packet_boundary_flag().Write(
emboss::AclDataPacketBoundaryFlag::FIRST_NON_FLUSHABLE);
writer.broadcast_flag().Write(
emboss::AclDataPacketBroadcastFlag::POINT_TO_POINT);
writer.data_total_length().Write(0);
PW_CHECK(writer.Ok());
buf->PushBack(std::move(alloc));
sniff_offload_manager().ProcessAclPacket(std::move(buf), direction);
SyncWithDispatcher();
}
SniffOffloadManagerTest::ControllerEvent
SniffOffloadManagerTest::MakeLeGetVendorCapabilitiesCommandCompleteEvent(
size_t size,
emboss::StatusCode status,
uint8_t version_major,
uint8_t version_minor) {
auto alloc = internal_allocator().MakeUnique<std::byte[]>(size);
PW_CHECK(alloc != nullptr, "Test alloc failed");
memset(alloc.get(), 0, alloc.size());
MultiBuf::Instance buf{internal_allocator()};
PW_CHECK(buf->TryReserveForPushBack());
vendor::android_hci::LEGetVendorCapabilitiesCommandCompleteEventWriter writer(
alloc.get(), size);
// We do not check IsComplete() for the entire writer because the buffer is
// artificially small. Instead check IsComplete() per-field before writing.
if (writer.command_complete().header().event_code().IsComplete()) {
writer.command_complete().header().event_code().Write(
emboss::EventCode::COMMAND_COMPLETE);
}
if (writer.command_complete().header().parameter_total_size().IsComplete()) {
writer.command_complete().header().parameter_total_size().Write(size - 2);
}
if (writer.command_complete().num_hci_command_packets().IsComplete()) {
writer.command_complete().num_hci_command_packets().Write(1);
}
if (writer.command_complete().command_opcode().IsComplete()) {
writer.command_complete().command_opcode().Write(
emboss::OpCode::ANDROID_LE_GET_VENDOR_CAPABILITIES);
}
if (writer.status().IsComplete()) {
writer.status().Write(status);
}
if (writer.sniff_offload_support().IsComplete()) {
writer.sniff_offload_support().Write(
vendor::android_hci::Capability::NOT_CAPABLE);
}
if (writer.version_supported().IsComplete()) {
writer.version_supported().major_number().Write(version_major);
writer.version_supported().minor_number().Write(version_minor);
}
buf->PushBack(std::move(alloc));
return ControllerEvent{
std::move(buf),
EventCode{cpp23::to_underlying(emboss::EventCode::COMMAND_COMPLETE)}};
}
MultiBuf::Instance SniffOffloadManagerTest::MakeZeroBuffer(size_t size) {
MultiBuf::Instance buf{internal_allocator()};
PW_CHECK(buf->TryReserveForPushBack());
if (size > 0) {
auto alloc = internal_allocator().MakeUnique<std::byte[]>(size);
PW_CHECK(alloc != nullptr, "Test alloc failed");
memset(alloc.get(), 0, alloc.size());
buf->PushBack(std::move(alloc));
}
return buf;
}
#define VERIFY_OR_RETURN(pred) \
do { \
if (!(pred)) { \
PW_LOG_INFO("%s:%d: `" #pred "` unsatisfied.", __FILE__, __LINE__); \
return false; \
} \
} while (0)
bool SniffOffloadManagerTest::CheckSniffSubratingSent(
std::optional<uint16_t> connection_handle) {
VERIFY_OR_RETURN(!packets_to_controller().empty());
VERIFY_OR_RETURN(packets_to_controller().front().completion ==
SniffOffloadManager::CompletionEvent::kCommandComplete);
std::array<std::byte, emboss::SniffSubratingCommand::IntrinsicSizeInBytes()>
scratch;
auto span = packets_to_controller().front().buf->Get(scratch);
emboss::SniffSubratingCommandView view(span.data(), span.size());
VERIFY_OR_RETURN(view.Ok());
VERIFY_OR_RETURN(view.header().opcode().Read() ==
emboss::OpCode::SNIFF_SUBRATING);
if (connection_handle.has_value()) {
VERIFY_OR_RETURN(view.connection_handle().Read() == *connection_handle);
}
packets_to_controller().pop_front();
return true;
}
bool SniffOffloadManagerTest::CheckSniffModeSent(
std::optional<uint16_t> connection_handle) {
VERIFY_OR_RETURN(!packets_to_controller().empty());
std::array<std::byte, emboss::SniffModeCommand::IntrinsicSizeInBytes()>
scratch;
auto span = packets_to_controller_.front().buf->Get(scratch);
emboss::SniffModeCommandView view(span.data(), span.size());
VERIFY_OR_RETURN(view.Ok());
VERIFY_OR_RETURN(view.header().opcode().Read() == emboss::OpCode::SNIFF_MODE);
if (connection_handle.has_value()) {
VERIFY_OR_RETURN(view.connection_handle().Read() == *connection_handle);
}
packets_to_controller_.pop_front();
return true;
}
bool SniffOffloadManagerTest::CheckSniffExitSent(
std::optional<uint16_t> connection_handle) {
VERIFY_OR_RETURN(!packets_to_controller().empty());
std::array<std::byte, emboss::ExitSniffModeCommand::IntrinsicSizeInBytes()>
scratch;
auto span = packets_to_controller().front().buf->Get(scratch);
emboss::ExitSniffModeCommandView view(span.data(), span.size());
VERIFY_OR_RETURN(view.Ok());
VERIFY_OR_RETURN(view.header().opcode().Read() ==
emboss::OpCode::EXIT_SNIFF_MODE);
if (connection_handle.has_value()) {
VERIFY_OR_RETURN(view.connection_handle().Read() == *connection_handle);
}
packets_to_controller_.pop_front();
return true;
}
bool SniffOffloadManagerTest::CheckCommandCompleteEventSent(
std::optional<CommandOpcode> opcode,
std::optional<emboss::StatusCode> status) {
VERIFY_OR_RETURN(!packets_to_host().empty());
std::array<std::byte,
emboss::SimpleCommandCompleteEvent::IntrinsicSizeInBytes()>
scratch;
auto span = packets_to_host().front()->Get(scratch);
emboss::SimpleCommandCompleteEventView view(span.data(), span.size());
VERIFY_OR_RETURN(view.command_complete().Ok());
VERIFY_OR_RETURN(view.command_complete().header().event_code().Read() ==
emboss::EventCode::COMMAND_COMPLETE);
VERIFY_OR_RETURN(
view.command_complete().header().parameter_total_size().Read() ==
emboss::SimpleCommandCompleteEvent::IntrinsicSizeInBytes() -
emboss::EventHeader::IntrinsicSizeInBytes());
if (opcode.has_value()) {
VERIFY_OR_RETURN(view.command_complete().command_opcode().Read() ==
*opcode);
}
if (status.has_value()) {
VERIFY_OR_RETURN(view.status().Ok());
VERIFY_OR_RETURN(view.status().Read() == *status);
}
packets_to_host().pop_front();
return true;
}
bool SniffOffloadManagerTest::CheckCommandStatusEventSent(
std::optional<CommandOpcode> opcode,
std::optional<emboss::StatusCode> status) {
VERIFY_OR_RETURN(!packets_to_host().empty());
std::array<std::byte, emboss::CommandStatusEvent::IntrinsicSizeInBytes()>
scratch;
auto span = packets_to_host().front()->Get(scratch);
emboss::CommandStatusEventView view(span.data(), span.size());
VERIFY_OR_RETURN(view.Ok());
VERIFY_OR_RETURN(view.header().event_code().Read() ==
emboss::EventCode::COMMAND_STATUS);
if (opcode.has_value()) {
VERIFY_OR_RETURN(view.command_opcode_enum().Read() == *opcode);
}
if (status.has_value()) {
VERIFY_OR_RETURN(view.status().Read() == *status);
}
packets_to_host().pop_front();
return true;
}
bool SniffOffloadManagerTest::CheckLeGetVendorCapabilitiesCommandCompleteEvent(
const MultiBuf& event,
std::optional<emboss::StatusCode> expected_status,
std::optional<vendor::android_hci::Capability>
expected_sniff_offload_support,
std::optional<uint8_t> expected_version_major,
std::optional<uint8_t> expected_version_minor) {
std::array<std::byte,
vendor::android_hci::LEGetVendorCapabilitiesCommandCompleteEvent::
IntrinsicSizeInBytes()>
scratch;
auto span = event.Get(scratch);
vendor::android_hci::LEGetVendorCapabilitiesCommandCompleteEventView view(
span.data(), span.size());
VERIFY_OR_RETURN(view.Ok());
VERIFY_OR_RETURN(view.command_complete().header().event_code().Read() ==
emboss::EventCode::COMMAND_COMPLETE);
VERIFY_OR_RETURN(view.command_complete().command_opcode().Read() ==
emboss::OpCode::ANDROID_LE_GET_VENDOR_CAPABILITIES);
VERIFY_OR_RETURN(
view.command_complete().header().parameter_total_size().Read() ==
event.size() - emboss::EventHeader::IntrinsicSizeInBytes());
if (expected_status.has_value()) {
VERIFY_OR_RETURN(view.status().Read() == *expected_status);
}
if (expected_sniff_offload_support.has_value()) {
VERIFY_OR_RETURN(view.sniff_offload_support().Read() ==
*expected_sniff_offload_support);
}
if (expected_version_major.has_value()) {
VERIFY_OR_RETURN(view.version_supported().major_number().Read() ==
*expected_version_major);
}
if (expected_version_minor.has_value()) {
VERIFY_OR_RETURN(view.version_supported().minor_number().Read() ==
*expected_version_minor);
}
return true;
}
#undef VERIFY_OR_RETURN
} // namespace
} // namespace pw::bluetooth::proxy::hci