OSDN Git Service

test_vendor: Refactor controller timing
authorMyles Watson <mylesgw@google.com>
Thu, 16 Jun 2016 10:54:23 +0000 (03:54 -0700)
committerMyles Watson <mylesgw@google.com>
Wed, 7 Sep 2016 23:14:13 +0000 (23:14 +0000)
Use AsyncManager to implement a timer tick and an event queue.

Change-Id: Iec2a0ef06f17ecce0652ecc52f5ef732fc4af026
Signed-off-by: Myles Watson <mylesgw@google.com>
vendor_libs/test_vendor_lib/include/dual_mode_controller.h
vendor_libs/test_vendor_lib/include/hci_transport.h
vendor_libs/test_vendor_lib/src/dual_mode_controller.cc
vendor_libs/test_vendor_lib/src/hci_transport.cc
vendor_libs/test_vendor_lib/src/vendor_manager.cc
vendor_libs/test_vendor_lib/test/hci_transport_unittest.cc

index 6793e31..a28b3be 100644 (file)
@@ -23,6 +23,7 @@
 #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"
@@ -181,16 +182,22 @@ class DualModeController {
   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).
 
@@ -324,6 +331,8 @@ class DualModeController {
   // 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
@@ -443,6 +452,11 @@ class DualModeController {
   // 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 {
@@ -456,6 +470,10 @@ class DualModeController {
     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;
@@ -477,12 +495,19 @@ class DualModeController {
 
   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.
@@ -520,6 +545,10 @@ class DualModeController {
 
   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;
 };
index 23d0eea..f63c5b9 100644 (file)
 #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"
@@ -35,7 +29,7 @@ extern "C" {
 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();
@@ -59,45 +53,17 @@ class 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_;
 
index 0410080..f9fea2c 100644 (file)
@@ -73,6 +73,11 @@ bool ParseUint16t(const base::StringPiece& value, uint16_t* field) {
 
 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(
@@ -179,6 +184,24 @@ void DualModeController::RegisterHandlersWithTestChannelTransport(
       });
 }
 
+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)
@@ -209,26 +232,42 @@ void DualModeController::RegisterEventChannel(
   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(
@@ -249,22 +288,27 @@ void DualModeController::TestChannelTimeoutAll(
 }
 
 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);
 }
 
@@ -494,8 +538,8 @@ void DualModeController::HciInquiry(const vector<uint8_t>& args) {
       /* 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(
@@ -506,6 +550,14 @@ 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");
index 6e73610..e2cfcc4 100644 (file)
 
 #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 {
 
@@ -49,7 +46,7 @@ int HciTransport::GetVendorFd() const {
 
 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;
@@ -58,92 +55,42 @@ bool HciTransport::SetUp() {
   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
index 07cc86a..cdfabb6 100644 (file)
@@ -41,25 +41,30 @@ bool VendorManager::Initialize() {
   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;
   }
index b9b7c63..bd69030 100644 (file)
@@ -51,7 +51,12 @@ class HciTransportTest : public ::testing::Test {
     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_;
@@ -59,7 +64,6 @@ class HciTransportTest : public ::testing::Test {
     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();
   }
 
@@ -70,7 +74,6 @@ class HciTransportTest : public ::testing::Test {
     EXPECT_EQ(HCI_RESET, command->GetOpcode());
     EXPECT_EQ(static_cast<size_t>(1), command->GetPayloadSize());
     if (command_callback_count_ == kMultiIterations) {
-      transport_.CloseVendorFd();
       SignalCommandhandlerFinished();
     }
   }
@@ -92,14 +95,13 @@ class HciTransportTest : public ::testing::Test {
   }
 
  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() {
@@ -133,7 +135,4 @@ TEST_F(HciTransportTest, MultiCommandCallback) {
   EXPECT_EQ(kMultiIterations, command_callback_count_);
 }
 
-// TODO(dennischeng): Add tests for PostEventResponse and
-// PostDelayedEventResponse.
-
 }  // namespace test_vendor_lib