Use AsyncManager to implement a timer tick and an event queue.
Change-Id: Iec2a0ef06f17ecce0652ecc52f5ef732fc4af026
Signed-off-by: Myles Watson <mylesgw@google.com>
#include <vector>
using std::vector;
+#include "async_manager.h"
#include "base/json/json_value_converter.h"
#include "base/time/time.h"
#include "command_packet.h"
void RegisterHandlersWithTestChannelTransport(
TestChannelTransport& transport);
+ // Set the callbacks for scheduling tasks.
+ void RegisterTaskScheduler(
+ std::function<AsyncTaskId(std::chrono::milliseconds, const TaskCallback&)>
+ evtScheduler);
+
+ void RegisterPeriodicTaskScheduler(
+ std::function<AsyncTaskId(std::chrono::milliseconds,
+ std::chrono::milliseconds,
+ const TaskCallback&)> periodicEvtScheduler);
+
+ void RegisterTaskCancel(std::function<void(AsyncTaskId)> cancel);
+
// Sets the callback to be used for sending events back to the HCI.
- // TODO(dennischeng): Once PostDelayedTask works, get rid of this and only use
- // |RegisterDelayedEventChannel|.
void RegisterEventChannel(
const std::function<void(std::unique_ptr<EventPacket>)>& send_event);
- void RegisterDelayedEventChannel(
- const std::function<void(std::unique_ptr<EventPacket>,
- std::chrono::milliseconds)>& send_event);
-
// Controller commands. For error codes, see the Bluetooth Core Specification,
// Version 4.2, Volume 2, Part D (page 370).
// Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.1.1
void HciInquiry(const vector<uint8_t>& args);
+ void InquiryTimeout();
+
// OGF: 0x0001
// OCF: 0x0002
// Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.1.2
// Causes all future HCI commands to timeout.
void TestChannelTimeoutAll(const vector<std::string>& args);
+ void HandleTimerTick();
+ void SetTimerPeriod(std::chrono::milliseconds new_period);
+ void StartTimer();
+ void StopTimer();
+
private:
// Current link layer state of the controller.
enum State {
kDelayedResponse, // Event responses are sent after a delay.
};
+ // Set a timer for a future action
+ void AddControllerEvent(std::chrono::milliseconds,
+ const TaskCallback& callback);
+
// Creates a command complete event and sends it back to the HCI.
void SendCommandComplete(const uint16_t command_opcode,
const vector<uint8_t>& return_parameters) const;
void SetEventDelay(int64_t delay);
+ // Callbacks to schedule tasks.
+ std::function<AsyncTaskId(std::chrono::milliseconds, const TaskCallback&)>
+ schedule_task_;
+ std::function<AsyncTaskId(std::chrono::milliseconds,
+ std::chrono::milliseconds,
+ const TaskCallback&)>
+ schedule_periodic_task_;
+
+ std::function<void(AsyncTaskId)> cancel_task_;
+
// Callback provided to send events from the controller back to the HCI.
std::function<void(std::unique_ptr<EventPacket>)> send_event_;
- std::function<void(std::unique_ptr<EventPacket>, std::chrono::milliseconds)>
- send_delayed_event_;
-
// Maintains the commands to be registered and used in the HciHandler object.
// Keys are command opcodes and values are the callbacks to handle each
// command.
TestChannelState test_channel_state_;
+ std::vector<AsyncTaskId> controller_events_;
+ AsyncTaskId timer_tick_task_;
+ std::chrono::milliseconds timer_period_ = std::chrono::milliseconds(1000);
+
DualModeController(const DualModeController& cmdPckt) = delete;
DualModeController& operator=(const DualModeController& cmdPckt) = delete;
};
#include <list>
#include <memory>
-extern "C" {
-#include <sys/epoll.h>
-} // extern "C"
-
-#include "async_manager.h"
#include "base/files/scoped_file.h"
-#include "base/time/time.h"
#include "command_packet.h"
#include "event_packet.h"
#include "packet.h"
namespace test_vendor_lib {
// Manages communication channel between HCI and the controller by providing the
-// socketing mechanisms for reading/writing between the HCI and the controller.
+// socket mechanisms for sending HCI [commands|events] [to|from] the controller.
class HciTransport {
public:
HciTransport();
void RegisterCommandHandler(
const std::function<void(std::unique_ptr<CommandPacket>)>& callback);
- // Sets the callback that is to schedule events.
- void RegisterEventScheduler(
- const std::function<void(std::chrono::milliseconds, const TaskCallback&)>&
- evtScheduler);
-
- // Sets the callback that is to schedule events.
- void RegisterPeriodicEventScheduler(
- const std::function<void(std::chrono::milliseconds,
- std::chrono::milliseconds,
- const TaskCallback&)>& periodicEvtScheduler);
-
- // Posts the event onto |outbound_events_| to be written sometime in the
- // future when the vendor file descriptor is ready for writing.
- void PostEventResponse(const EventPacket& event);
+ // Blocks while it tries to writes the event to the vendor file descriptor.
+ void PostEvent(const EventPacket& event);
- // Posts the event onto |outbound_events_| after |delay| ms. A call to
- // |PostEventResponse| with |delay| 0 is equivalent to a call to |PostEvent|.
- void PostDelayedEventResponse(const EventPacket& event,
- std::chrono::milliseconds delay);
-
- void OnFileCanReadWithoutBlocking(int fd);
+ // Called when there is a command to read on |fd|.
+ void OnCommandReady(int fd);
private:
- // Reads in a command packet and calls the command ready callback,
- // |command_handler_|, passing ownership of the command packet to the handler.
- void ReceiveReadyCommand() const;
-
// Callback executed in ReceiveReadyCommand() to pass the incoming command
// over to the handler for further processing.
std::function<void(std::unique_ptr<CommandPacket>)> command_handler_;
- // Callbacks to schedule events.
- std::function<void(std::chrono::milliseconds, const TaskCallback&)>
- schedule_event_;
- std::function<void(std::chrono::milliseconds,
- std::chrono::milliseconds,
- const TaskCallback&)>
- schedule_periodic_event_;
-
// For performing packet-based IO.
PacketStream packet_stream_;
namespace test_vendor_lib {
+void DualModeController::AddControllerEvent(std::chrono::milliseconds delay,
+ const TaskCallback& task) {
+ controller_events_.push_back(schedule_task_(delay, task));
+}
+
void DualModeController::SendCommandCompleteSuccess(
const uint16_t command_opcode) const {
send_event_(EventPacket::CreateCommandCompleteOnlyStatusEvent(
});
}
+void DualModeController::RegisterTaskScheduler(
+ std::function<AsyncTaskId(std::chrono::milliseconds, const TaskCallback&)>
+ oneshotScheduler) {
+ schedule_task_ = oneshotScheduler;
+}
+
+void DualModeController::RegisterPeriodicTaskScheduler(
+ std::function<AsyncTaskId(std::chrono::milliseconds,
+ std::chrono::milliseconds,
+ const TaskCallback&)> periodicScheduler) {
+ schedule_periodic_task_ = periodicScheduler;
+}
+
+void DualModeController::RegisterTaskCancel(
+ std::function<void(AsyncTaskId)> task_cancel) {
+ cancel_task_ = task_cancel;
+}
+
void DualModeController::HandleTestChannelCommand(
const std::string& name, const vector<std::string>& args) {
if (active_test_channel_commands_.count(name) == 0)
send_event_ = callback;
}
-void DualModeController::RegisterDelayedEventChannel(
- const std::function<void(std::unique_ptr<EventPacket>,
- std::chrono::milliseconds)>& callback) {
- send_delayed_event_ = callback;
- SetEventDelay(0);
+void DualModeController::HandleTimerTick() {
+ // PageScan();
+ if (le_scan_enable_)
+ LOG_ERROR(LOG_TAG, "LE scan");
+ // LeScan();
}
-void DualModeController::SetEventDelay(int64_t delay) {
- if (delay < 0)
- delay = 0;
- send_event_ = [this, delay](std::unique_ptr<EventPacket> arg) {
- send_delayed_event_(std::move(arg), std::chrono::milliseconds(delay));
- };
+void DualModeController::SetTimerPeriod(std::chrono::milliseconds new_period) {
+ timer_period_ = new_period;
+
+ if (timer_tick_task_ == 0)
+ return;
+
+ // Restart the timer with the new period
+ StopTimer();
+ StartTimer();
+}
+
+void DualModeController::StartTimer() {
+ LOG_ERROR(LOG_TAG, "StartTimer");
+ timer_tick_task_ = schedule_periodic_task_(
+ std::chrono::milliseconds(0), timer_period_, [this]() {
+ DualModeController::HandleTimerTick();
+ });
+}
+
+void DualModeController::StopTimer() {
+ LOG_ERROR(LOG_TAG, "StopTimer");
+ cancel_task_(timer_tick_task_);
+ timer_tick_task_ = 0;
}
void DualModeController::TestChannelClear(
UNUSED_ATTR const vector<std::string>& args) {
LogCommand("TestChannel Clear");
test_channel_state_ = kNone;
- SetEventDelay(0);
}
void DualModeController::TestChannelDiscover(
}
void DualModeController::TestChannelSetEventDelay(
- const vector<std::string>& args) {
+ const vector<std::string>& args UNUSED_ATTR) {
LogCommand("TestChannel Set Event Delay");
test_channel_state_ = kDelayedResponse;
- SetEventDelay(std::stoi(args[1]));
}
void DualModeController::TestChannelClearEventDelay(
UNUSED_ATTR const vector<std::string>& args) {
LogCommand("TestChannel Clear Event Delay");
test_channel_state_ = kNone;
- SetEventDelay(0);
}
void DualModeController::HciReset(UNUSED_ATTR const vector<uint8_t>& args) {
LogCommand("Reset");
state_ = kStandby;
+ if (timer_tick_task_ != 0) {
+ LOG_INFO(LOG_TAG, "The timer was already running!");
+ StopTimer();
+ }
+ LOG_INFO(LOG_TAG, "Starting timer.");
+ StartTimer();
+
SendCommandCompleteSuccess(HCI_RESET);
}
/* TODO: Return responses from modeled devices */
} break;
}
- send_delayed_event_(EventPacket::CreateInquiryCompleteEvent(kSuccessStatus),
- std::chrono::milliseconds(args[4] * 1280));
+ AddControllerEvent(std::chrono::milliseconds(args[4] * 1280),
+ [this]() { DualModeController::InquiryTimeout(); });
}
void DualModeController::HciInquiryCancel(
SendCommandCompleteSuccess(HCI_INQUIRY_CANCEL);
}
+void DualModeController::InquiryTimeout() {
+ LOG_INFO(LOG_TAG, "InquiryTimer fired");
+ if (state_ == kInquiry) {
+ state_ = kStandby;
+ send_event_(EventPacket::CreateInquiryCompleteEvent(kSuccessStatus));
+ }
+}
+
void DualModeController::HciDeleteStoredLinkKey(
UNUSED_ATTR const vector<uint8_t>& args) {
LogCommand("Delete Stored Link Key");
#define LOG_TAG "hci_transport"
-#include <cinttypes>
-
#include "hci_transport.h"
-extern "C" {
+#include <assert.h>
#include <sys/socket.h>
#include "osi/include/log.h"
#include "stack/include/hcidefs.h"
-} // extern "C"
namespace test_vendor_lib {
bool HciTransport::SetUp() {
int socketpair_fds[2];
- // TODO(dennischeng): Use SOCK_SEQPACKET here.
+
const int success = socketpair(AF_LOCAL, SOCK_STREAM, 0, socketpair_fds);
if (success < 0)
return false;
return true;
}
-void HciTransport::OnFileCanReadWithoutBlocking(int fd) {
+void HciTransport::OnCommandReady(int fd) {
CHECK(fd == GetVendorFd());
- LOG_INFO(LOG_TAG, "Event ready in HciTransport on fd: %d.", fd);
+ LOG_VERBOSE(LOG_TAG, "Command ready in HciTransport on fd: %d.", fd);
const serial_data_type_t packet_type = packet_stream_.ReceivePacketType(fd);
switch (packet_type) {
case (DATA_TYPE_COMMAND): {
- ReceiveReadyCommand();
+ command_handler_(packet_stream_.ReceiveCommand(fd));
break;
}
case (DATA_TYPE_ACL): {
- LOG_INFO(LOG_TAG, "ACL data packets not currently supported.");
+ LOG_ERROR(LOG_TAG, "ACL data packets not currently supported.");
break;
}
case (DATA_TYPE_SCO): {
- LOG_INFO(LOG_TAG, "SCO data packets not currently supported.");
+ LOG_ERROR(LOG_TAG, "SCO data packets not currently supported.");
break;
}
- // TODO(dennischeng): Add debug level assert here.
default: {
- LOG_INFO(LOG_TAG, "Error received an invalid packet type from the HCI.");
+ LOG_ERROR(LOG_TAG, "Error received an invalid packet type from the HCI.");
+ assert(packet_type == DATA_TYPE_COMMAND);
break;
}
}
}
-void HciTransport::ReceiveReadyCommand() const {
- std::unique_ptr<CommandPacket> command =
- packet_stream_.ReceiveCommand(GetVendorFd());
- LOG_INFO(LOG_TAG, "Received command packet.");
- command_handler_(std::move(command));
-}
-
void HciTransport::RegisterCommandHandler(
const std::function<void(std::unique_ptr<CommandPacket>)>& callback) {
command_handler_ = callback;
}
-void HciTransport::RegisterEventScheduler(
- const std::function<void(std::chrono::milliseconds, const TaskCallback&)>&
- evtScheduler) {
- schedule_event_ = evtScheduler;
-}
-
-void HciTransport::RegisterPeriodicEventScheduler(
- const std::function<void(std::chrono::milliseconds,
- std::chrono::milliseconds,
- const TaskCallback&)>& periodicEvtScheduler) {
- schedule_periodic_event_ = periodicEvtScheduler;
-}
-
-void HciTransport::PostEventResponse(const EventPacket& event) {
- schedule_event_(std::chrono::milliseconds(0), [this, event]() {
- packet_stream_.SendEvent(event, GetVendorFd());
- });
-}
-
-void HciTransport::PostDelayedEventResponse(const EventPacket& event,
- std::chrono::milliseconds delay) {
- // TODO(dennischeng): When it becomes available for MessageLoopForIO, use the
- // thread's task runner to post |PostEventResponse| as a delayed task, being
- // sure to CHECK the appropriate task runner attributes using
- // base::ThreadTaskRunnerHandle.
-
- // The system does not support high resolution timing and the clock could be
- // as coarse as ~15.6 ms so the event is sent without a delay to avoid
- // inconsistent event responses.
- if (!base::TimeTicks::IsHighResolution()) {
- LOG_INFO(LOG_TAG,
- "System does not support high resolution timing. Sending event "
- "without delay.");
- schedule_event_(std::chrono::milliseconds(0), [this, event]() {
- packet_stream_.SendEvent(event, GetVendorFd());
- });
- }
-
- LOG_INFO(LOG_TAG,
- "Posting event response with delay of %" PRId64 " ms.",
- static_cast<int64_t>(std::chrono::milliseconds(delay).count()));
-
- schedule_event_(delay, [this, event]() {
- packet_stream_.SendEvent(event, GetVendorFd());
- });
+void HciTransport::PostEvent(const EventPacket& event) {
+ packet_stream_.SendEvent(event, GetVendorFd());
}
} // namespace test_vendor_lib
controller_.RegisterHandlersWithTestChannelTransport(test_channel_transport_);
controller_.RegisterEventChannel([this](std::unique_ptr<EventPacket> event) {
- transport_.PostEventResponse(*event);
+ EventPacket evt = *event;
+ async_manager_.ExecAsync(std::chrono::milliseconds(0), [evt,this]() {
+ transport_.PostEvent(evt);
+ });
});
- transport_.RegisterEventScheduler(
+ controller_.RegisterTaskScheduler(
[this](std::chrono::milliseconds delay, const TaskCallback& task) {
- async_manager_.ExecAsync(delay, task);
+ return async_manager_.ExecAsync(delay, task);
});
- transport_.RegisterPeriodicEventScheduler(
+ controller_.RegisterPeriodicTaskScheduler(
[this](std::chrono::milliseconds delay,
std::chrono::milliseconds period,
const TaskCallback& task) {
- async_manager_.ExecAsyncPeriodically(delay, period, task);
+ return async_manager_.ExecAsyncPeriodically(delay, period, task);
});
+ controller_.RegisterTaskCancel(
+ [this](AsyncTaskId task) { async_manager_.CancelAsyncTask(task); });
+
if (async_manager_.WatchFdForNonBlockingReads(
- transport_.GetVendorFd(), [this](int fd) {
- transport_.OnFileCanReadWithoutBlocking(fd);
- }) != 0) {
+ transport_.GetVendorFd(),
+ [this](int fd) { transport_.OnCommandReady(fd); }) != 0) {
LOG_ERROR(LOG_TAG, "Error watching vendor fd.");
return true;
}
StartThread();
}
- ~HciTransportTest() { transport_.CloseHciFd(); }
+ ~HciTransportTest() {
+ async_manager_.StopWatchingFileDescriptor(transport_.GetVendorFd());
+ transport_.CloseVendorFd();
+ async_manager_.StopWatchingFileDescriptor(transport_.GetHciFd());
+ transport_.CloseHciFd();
+ }
void CommandCallback(std::unique_ptr<CommandPacket> command) {
++command_callback_count_;
EXPECT_EQ(DATA_TYPE_COMMAND, command->GetType());
EXPECT_EQ(HCI_RESET, command->GetOpcode());
EXPECT_EQ(static_cast<size_t>(1), command->GetPayloadSize());
- transport_.CloseVendorFd();
SignalCommandhandlerFinished();
}
EXPECT_EQ(HCI_RESET, command->GetOpcode());
EXPECT_EQ(static_cast<size_t>(1), command->GetPayloadSize());
if (command_callback_count_ == kMultiIterations) {
- transport_.CloseVendorFd();
SignalCommandhandlerFinished();
}
}
}
private:
- // Workaround because ASSERT cannot be used directly in a constructor
+ // Workarounds because ASSERT cannot be used directly in a constructor
void SetUpTransport() { ASSERT_TRUE(transport_.SetUp()); }
void StartThread() {
ASSERT_TRUE(async_manager_.WatchFdForNonBlockingReads(
- transport_.GetVendorFd(), [this](int fd) {
- transport_.OnFileCanReadWithoutBlocking(fd);
- }) == 0);
+ transport_.GetVendorFd(),
+ [this](int fd) { transport_.OnCommandReady(fd); }) == 0);
}
void SignalCommandhandlerFinished() {
EXPECT_EQ(kMultiIterations, command_callback_count_);
}
-// TODO(dennischeng): Add tests for PostEventResponse and
-// PostDelayedEventResponse.
-
} // namespace test_vendor_lib